From f4b25b172af95bb22554789fef2ce0ea3453cacb Mon Sep 17 00:00:00 2001 From: Michael Loughry Date: Thu, 6 Aug 2020 20:11:20 +0000 Subject: [PATCH 1/9] Copy ContextualMenu to react-next --- packages/react-next/etc/react-next.api.md | 337 +++- packages/react-next/src/ContextualMenu.ts | 2 +- .../react-next/src/common/ThemingSass.scss | 6 + packages/react-next/src/common/_common.scss | 10 + packages/react-next/src/common/_effects.scss | 8 + .../react-next/src/common/_focusBorder.scss | 63 + .../react-next/src/common/_highContrast.scss | 17 + packages/react-next/src/common/_i18n.scss | 295 ++++ .../src/common/_legacyThemePalette.scss | 19 + .../react-next/src/common/_semanticSlots.scss | 101 ++ .../src/common/_themeOverrides.scss | 51 + .../src/common/_themeVariables.scss | 118 ++ .../src/common/sharedIsConformant.ts | 2 +- .../react-next/src/common/testUtilities.ts | 7 +- .../ContextualMenu/ContextualMenu.base.tsx | 1382 +++++++++++++++++ .../ContextualMenu.classNames.ts | 258 +++ .../ContextualMenu/ContextualMenu.cnstyles.ts | 247 +++ .../ContextualMenu.deprecated.test.tsx | 309 ++++ .../ContextualMenu/ContextualMenu.doc.tsx | 126 ++ .../ContextualMenu/ContextualMenu.styles.ts | 85 + .../ContextualMenu/ContextualMenu.test.tsx | 1313 ++++++++++++++++ .../ContextualMenu/ContextualMenu.tsx | 21 + .../ContextualMenu/ContextualMenu.types.ts | 655 ++++++++ .../ContextualMenuItem.base.tsx | 114 ++ .../ContextualMenuItem.test.tsx | 177 +++ .../ContextualMenu/ContextualMenuItem.ts | 18 + .../ContextualMenuItem.types.ts | 248 +++ .../ContextualMenuAnchor.deprecated.test.tsx | 49 + .../ContextualMenuAnchor.test.tsx | 100 ++ .../ContextualMenuAnchor.tsx | 114 ++ .../ContextualMenuButton.deprecated.test.tsx | 49 + .../ContextualMenuButton.test.tsx | 112 ++ .../ContextualMenuButton.tsx | 116 ++ .../ContextualMenuItemWrapper.tsx | 61 + .../ContextualMenuItemWrapper.types.ts | 143 ++ ...textualMenuSplitButton.deprecated.test.tsx | 49 + .../ContextualMenuSplitButton.test.tsx | 76 + .../ContextualMenuSplitButton.tsx | 327 ++++ ...textualMenuAnchor.deprecated.test.tsx.snap | 21 + .../ContextualMenuAnchor.test.tsx.snap | 21 + ...textualMenuButton.deprecated.test.tsx.snap | 20 + .../ContextualMenuButton.test.tsx.snap | 20 + ...alMenuSplitButton.deprecated.test.tsx.snap | 66 + .../ContextualMenuSplitButton.test.tsx.snap | 66 + .../ContextualMenuItemWrapper/index.ts | 5 + .../ContextualMenu.test.tsx.snap | 496 ++++++ .../ContextualMenuItem.test.tsx.snap | 11 + .../docs/ContextualMenuBestPractices.md | 0 .../docs/ContextualMenuDonts.md | 4 + .../ContextualMenu/docs/ContextualMenuDos.md | 4 + .../docs/ContextualMenuOverview.md | 5 + .../examples/ContextualMenu.Basic.Example.tsx | 85 + .../ContextualMenu.Checkmarks.Example.tsx | 216 +++ .../ContextualMenu.CustomMenuItem.Example.tsx | 47 + .../ContextualMenu.CustomMenuList.Example.tsx | 125 ++ .../ContextualMenu.Customization.Example.tsx | 225 +++ ...alMenu.CustomizationWithNoWrap.Example.tsx | 238 +++ .../ContextualMenu.Default.Example.tsx | 70 + .../ContextualMenu.Directional.Example.tsx | 131 ++ .../ContextualMenu.Header.Example.tsx | 94 ++ .../examples/ContextualMenu.Icon.Example.tsx | 96 ++ ...textualMenu.Icon.SecondaryText.Example.tsx | 47 + .../ContextualMenu.Persisted.Example.tsx | 86 + .../ContextualMenu.ScrollBar.Example.tsx | 41 + .../ContextualMenu.Section.Example.tsx | 71 + .../ContextualMenu.Submenu.Example.tsx | 142 ++ .../examples/ContextualMenuExample.scss | 94 ++ .../src/components/ContextualMenu/index.ts | 6 + .../contextualMenuUtility.test.ts | 116 ++ .../contextualMenu/contextualMenuUtility.ts | 40 + .../src/utilities/contextualMenu/index.ts | 1 + 71 files changed, 9880 insertions(+), 15 deletions(-) create mode 100644 packages/react-next/src/common/ThemingSass.scss create mode 100644 packages/react-next/src/common/_common.scss create mode 100644 packages/react-next/src/common/_effects.scss create mode 100644 packages/react-next/src/common/_focusBorder.scss create mode 100644 packages/react-next/src/common/_highContrast.scss create mode 100644 packages/react-next/src/common/_i18n.scss create mode 100644 packages/react-next/src/common/_legacyThemePalette.scss create mode 100644 packages/react-next/src/common/_semanticSlots.scss create mode 100644 packages/react-next/src/common/_themeOverrides.scss create mode 100644 packages/react-next/src/common/_themeVariables.scss create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenu.base.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenu.classNames.ts create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenu.cnstyles.ts create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenu.deprecated.test.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenu.doc.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenu.styles.ts create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenu.test.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenu.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenu.types.ts create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItem.base.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItem.test.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItem.ts create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItem.types.ts create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuAnchor.deprecated.test.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuAnchor.test.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuAnchor.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuButton.deprecated.test.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuButton.test.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuButton.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuItemWrapper.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuItemWrapper.types.ts create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuSplitButton.deprecated.test.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuSplitButton.test.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuSplitButton.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/__snapshots__/ContextualMenuAnchor.deprecated.test.tsx.snap create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/__snapshots__/ContextualMenuAnchor.test.tsx.snap create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/__snapshots__/ContextualMenuButton.deprecated.test.tsx.snap create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/__snapshots__/ContextualMenuButton.test.tsx.snap create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/__snapshots__/ContextualMenuSplitButton.deprecated.test.tsx.snap create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/__snapshots__/ContextualMenuSplitButton.test.tsx.snap create mode 100644 packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/index.ts create mode 100644 packages/react-next/src/components/ContextualMenu/__snapshots__/ContextualMenu.test.tsx.snap create mode 100644 packages/react-next/src/components/ContextualMenu/__snapshots__/ContextualMenuItem.test.tsx.snap create mode 100644 packages/react-next/src/components/ContextualMenu/docs/ContextualMenuBestPractices.md create mode 100644 packages/react-next/src/components/ContextualMenu/docs/ContextualMenuDonts.md create mode 100644 packages/react-next/src/components/ContextualMenu/docs/ContextualMenuDos.md create mode 100644 packages/react-next/src/components/ContextualMenu/docs/ContextualMenuOverview.md create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Basic.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Checkmarks.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.CustomMenuItem.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.CustomMenuList.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Customization.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.CustomizationWithNoWrap.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Default.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Directional.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Header.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Icon.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Icon.SecondaryText.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Persisted.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.ScrollBar.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Section.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Submenu.Example.tsx create mode 100644 packages/react-next/src/components/ContextualMenu/examples/ContextualMenuExample.scss create mode 100644 packages/react-next/src/components/ContextualMenu/index.ts create mode 100644 packages/react-next/src/utilities/contextualMenu/contextualMenuUtility.test.ts create mode 100644 packages/react-next/src/utilities/contextualMenu/contextualMenuUtility.ts create mode 100644 packages/react-next/src/utilities/contextualMenu/index.ts diff --git a/packages/react-next/etc/react-next.api.md b/packages/react-next/etc/react-next.api.md index e4bc2f6805fa3..a3ffca0f56364 100644 --- a/packages/react-next/etc/react-next.api.md +++ b/packages/react-next/etc/react-next.api.md @@ -6,16 +6,18 @@ import { BaseSlots } from '@fluentui/react-compose'; import { ComposePreparedOptions } from '@fluentui/react-compose'; +import { DirectionalHint } from 'office-ui-fabric-react/lib/common/DirectionalHint'; import { IBaseFloatingPickerProps } from 'office-ui-fabric-react/lib/FloatingPicker'; import { IBaseProps } from 'office-ui-fabric-react/lib/Utilities'; import { IButtonProps } from '@fluentui/react-next/lib/compat/Button'; import { IButtonProps as IButtonProps_2 } from 'office-ui-fabric-react/lib/components/Button/Button.types'; -import { IButtonStyles } from '@fluentui/react-next/lib/compat/Button'; +import { IButtonStyles } from 'office-ui-fabric-react/lib/Button'; +import { IButtonStyles as IButtonStyles_2 } from '@fluentui/react-next/lib/compat/Button'; import { ICalloutPositionedInfo } from 'office-ui-fabric-react/lib/utilities/positioning'; import { IComponentAs } from 'office-ui-fabric-react/lib/Utilities'; -import { IContextualMenuProps } from 'office-ui-fabric-react/lib/ContextualMenu'; import { ICustomizerContext } from 'office-ui-fabric-react/lib/Utilities'; -import { IFocusZoneProps } from '@fluentui/react-focus'; +import { IFocusZoneProps } from 'office-ui-fabric-react/lib/FocusZone'; +import { IFocusZoneProps as IFocusZoneProps_2 } from '@fluentui/react-focus'; import { IIconProps } from 'office-ui-fabric-react/lib/Icon'; import { IKeytipProps } from 'office-ui-fabric-react/lib/Keytip'; import { ILayerProps } from 'office-ui-fabric-react/lib/Layer'; @@ -35,6 +37,7 @@ import { ISuggestionModel } from 'office-ui-fabric-react/lib/Pickers'; import { ISvgIconProps } from '@fluentui/react-icons'; import { ITeachingBubble } from 'office-ui-fabric-react/lib/TeachingBubble'; import { ITheme } from 'office-ui-fabric-react/lib/Styling'; +import { IVerticalDividerClassNames } from 'office-ui-fabric-react/src/components/Divider/VerticalDivider.types'; import { IWithResponsiveModeState } from 'office-ui-fabric-react/lib/utilities/decorators/withResponsiveMode'; import { Point } from 'office-ui-fabric-react/lib/Utilities'; import { Position } from 'office-ui-fabric-react/lib/utilities/positioning'; @@ -102,6 +105,9 @@ export class BaseSelectedItemsList> // @public (undocumented) export const Callout: React.ForwardRefExoticComponent>; +// @public +export function canAnyMenuItemsCheck(items: IContextualMenuItem[]): boolean; + // @public (undocumented) export const Checkbox: import("@fluentui/react-compose").ComponentWithAs<"div", ICheckboxProps>; @@ -128,12 +134,66 @@ export class ColorPickerGridCellBase extends React.PureComponent; + +// @public (undocumented) +export class ContextualMenuBase extends React.Component { + constructor(props: IContextualMenuProps); + // (undocumented) + componentDidMount(): void; + // (undocumented) + componentWillUnmount(): void; + // (undocumented) + static defaultProps: IContextualMenuProps; + // (undocumented) + dismiss: (ev?: any, dismissAll?: boolean | undefined) => void; + // (undocumented) + render(): JSX.Element | null; + // (undocumented) + shouldComponentUpdate(newProps: IContextualMenuProps, newState: IContextualMenuState): boolean; + // (undocumented) + UNSAFE_componentWillMount(): void; + // (undocumented) + UNSAFE_componentWillUpdate(newProps: IContextualMenuProps): void; + } + +// @public +export const ContextualMenuItem: React.FunctionComponent; + +// @public (undocumented) +export class ContextualMenuItemBase extends React.Component { + constructor(props: IContextualMenuItemProps); + // (undocumented) + dismissMenu: (dismissAll?: boolean | undefined) => void; + // (undocumented) + dismissSubMenu: () => void; + // (undocumented) + openSubMenu: () => void; + // (undocumented) + render(): JSX.Element; +} + +// @public (undocumented) +export enum ContextualMenuItemType { + // (undocumented) + Divider = 1, + // (undocumented) + Header = 2, + // (undocumented) + Normal = 0, + // (undocumented) + Section = 3 +} + // @public export const Customizer: React.FunctionComponent; // @public (undocumented) export const DEFAULT_MASK_CHAR = "_"; +export { DirectionalHint } + // @public (undocumented) export class ExtendedSelectedItem extends React.Component { constructor(props: ISelectedPeopleItemProps); @@ -188,6 +248,9 @@ export const getNextResizeGroupStateProvider: (measurementCache?: { // @public export function getPersonaInitialsColor(props: Pick): string; +// @public (undocumented) +export function getSubmenuItems(item: IContextualMenuItem): IContextualMenuItem[] | undefined; + // @public (undocumented) export interface IAccessiblePopupProps { closeButtonAriaLabel?: string; @@ -266,9 +329,9 @@ export interface ICalloutProps extends React.HTMLAttributes { className?: string; coverTarget?: boolean; // Warning: (ae-forgotten-export) The symbol "DirectionalHint" needs to be exported by the entry point index.d.ts - directionalHint?: DirectionalHint; + directionalHint?: DirectionalHint_2; directionalHintFixed?: boolean; - directionalHintForRTL?: DirectionalHint; + directionalHintForRTL?: DirectionalHint_2; doNotLayer?: boolean; finalHeight?: number; gapSpace?: number; @@ -533,6 +596,248 @@ export interface IColorPickerGridCellStyles { svg: IStyle; } +// @public (undocumented) +export interface IContextualMenu { +} + +// @public (undocumented) +export interface IContextualMenuItem { + [propertyName: string]: any; + ariaLabel?: string; + canCheck?: boolean; + checked?: boolean; + className?: string; + componentRef?: IRefObject; + customOnRenderListLength?: number; + data?: any; + disabled?: boolean; + // Warning: (ae-forgotten-export) The symbol "IMenuItemClassNames" needs to be exported by the entry point index.d.ts + // + // @deprecated + getItemClassNames?: (theme: ITheme, disabled: boolean, expanded: boolean, checked: boolean, isAnchorLink: boolean, knownIcon: boolean, itemClassName?: string, dividerClassName?: string, iconClassName?: string, subMenuClassName?: string, primaryDisabled?: boolean) => IMenuItemClassNames; + getSplitButtonVerticalDividerClassNames?: (theme: ITheme) => IVerticalDividerClassNames; + href?: string; + iconProps?: IIconProps; + // @deprecated + inactive?: boolean; + itemProps?: Partial; + // (undocumented) + itemType?: ContextualMenuItemType; + key: string; + keytipProps?: IKeytipProps; + // @deprecated + name?: string; + onClick?: (ev?: React.MouseEvent | React.KeyboardEvent, item?: IContextualMenuItem) => boolean | void; + onMouseDown?: (item: IContextualMenuItem, event: React.MouseEvent) => void; + onRender?: (item: any, dismissMenu: (ev?: any, dismissAll?: boolean) => void) => React.ReactNode; + onRenderIcon?: IRenderFunction; + primaryDisabled?: boolean; + rel?: string; + role?: string; + secondaryText?: string; + sectionProps?: IContextualMenuSection; + // @deprecated (undocumented) + shortCut?: string; + split?: boolean; + // @deprecated + style?: React.CSSProperties; + submenuIconProps?: IIconProps; + subMenuProps?: IContextualMenuProps; + target?: string; + text?: string; + title?: string; +} + +// @public (undocumented) +export interface IContextualMenuItemProps extends React.HTMLAttributes { + className?: string; + classNames: IMenuItemClassNames; + componentRef?: IRefObject; + dismissMenu?: (ev?: any, dismissAll?: boolean) => void; + dismissSubMenu?: () => void; + getSubmenuTarget?: () => HTMLElement | undefined; + hasIcons: boolean | undefined; + index: number; + item: IContextualMenuItem; + onCheckmarkClick?: (item: IContextualMenuItem, ev: React.MouseEvent) => void; + openSubMenu?: (item: any, target: HTMLElement) => void; + styles?: IStyleFunctionOrObject; + theme?: ITheme; +} + +// @public (undocumented) +export interface IContextualMenuItemRenderProps extends IContextualMenuItem { + // (undocumented) + focusableElementIndex: number; + // (undocumented) + hasCheckmarks: boolean; + // (undocumented) + hasIcons: boolean; + // (undocumented) + index: number; + // (undocumented) + totalItemCount: number; +} + +// @public (undocumented) +export interface IContextualMenuItemStyleProps { + checked: boolean; + className?: string; + disabled: boolean; + dividerClassName?: string; + expanded: boolean; + iconClassName?: string; + isAnchorLink: boolean; + itemClassName?: string; + knownIcon: boolean; + primaryDisabled?: boolean; + subMenuClassName?: string; + theme: ITheme; +} + +// @public (undocumented) +export interface IContextualMenuItemStyles extends IButtonStyles { + anchorLink: IStyle; + checkmarkIcon: IStyle; + divider: IStyle; + icon: IStyle; + iconColor: IStyle; + item: IStyle; + label: IStyle; + linkContent: IStyle; + linkContentMenu: IStyle; + root: IStyle; + secondaryText: IStyle; + splitContainer: IStyle; + splitMenu: IStyle; + splitPrimary: IStyle; + subMenuIcon: IStyle; +} + +// @public (undocumented) +export interface IContextualMenuListProps { + // (undocumented) + defaultMenuItemRenderer: (item: IContextualMenuItemRenderProps) => React.ReactNode; + // (undocumented) + hasCheckmarks: boolean; + // (undocumented) + hasIcons: boolean; + // (undocumented) + items: IContextualMenuItem[]; + // (undocumented) + role?: string; + // (undocumented) + totalItemCount: number; +} + +// @public (undocumented) +export interface IContextualMenuProps extends IBaseProps, IWithResponsiveModeState { + alignTargetEdge?: boolean; + ariaLabel?: string; + beakWidth?: number; + bounds?: IRectangle | ((target?: Target, targetWindow?: Window) => IRectangle | undefined); + calloutProps?: ICalloutProps; + className?: string; + componentRef?: IRefObject; + contextualMenuItemAs?: React.ComponentClass | React.FunctionComponent; + coverTarget?: boolean; + delayUpdateFocusOnHover?: boolean; + directionalHint?: DirectionalHint_2; + directionalHintFixed?: boolean; + directionalHintForRTL?: DirectionalHint_2; + doNotLayer?: boolean; + focusZoneProps?: IFocusZoneProps; + gapSpace?: number; + // Warning: (ae-forgotten-export) The symbol "IContextualMenuClassNames" needs to be exported by the entry point index.d.ts + // + // @deprecated + getMenuClassNames?: (theme: ITheme, className?: string) => IContextualMenuClassNames; + hidden?: boolean; + id?: string; + isBeakVisible?: boolean; + isSubMenu?: boolean; + items: IContextualMenuItem[]; + labelElementId?: string; + onDismiss?: (ev?: Event | React.MouseEvent | React.KeyboardEvent, dismissAll?: boolean) => void; + onItemClick?: (ev?: React.MouseEvent | React.KeyboardEvent, item?: IContextualMenuItem) => boolean | void; + onMenuDismissed?: (contextualMenu?: IContextualMenuProps) => void; + onMenuOpened?: (contextualMenu?: IContextualMenuProps) => void; + onRenderMenuList?: IRenderFunction; + onRenderSubMenu?: IRenderFunction; + shouldFocusOnContainer?: boolean; + shouldFocusOnMount?: boolean; + shouldUpdateWhenHidden?: boolean; + styles?: IStyleFunctionOrObject; + subMenuHoverDelay?: number; + target?: Target; + theme?: ITheme; + title?: string; + useTargetAsMinWidth?: boolean; + useTargetWidth?: boolean; +} + +// @public (undocumented) +export interface IContextualMenuRenderItem { + dismissMenu: (dismissAll?: boolean) => void; + dismissSubMenu: () => void; + openSubMenu: () => void; +} + +// @public (undocumented) +export interface IContextualMenuSection extends React.ClassAttributes { + bottomDivider?: boolean; + items: IContextualMenuItem[]; + title?: string; + topDivider?: boolean; +} + +// @public (undocumented) +export interface IContextualMenuState { + // (undocumented) + contextualMenuItems?: IContextualMenuItem[]; + // (undocumented) + contextualMenuTarget?: Element; + // (undocumented) + dismissedMenuItemKey?: string; + expandedByMouseClick?: boolean; + // (undocumented) + expandedMenuItemKey?: string; + // (undocumented) + positions?: any; + // (undocumented) + slideDirectionalClassName?: string; + // (undocumented) + submenuDirection?: DirectionalHint_2; + // (undocumented) + subMenuId?: string; + // (undocumented) + submenuTarget?: Element; +} + +// @public (undocumented) +export interface IContextualMenuStyleProps { + // (undocumented) + className?: string; + // (undocumented) + theme: ITheme; +} + +// @public (undocumented) +export interface IContextualMenuStyles { + container: IStyle; + header: IStyle; + list: IStyle; + root: IStyle; + subComponentStyles: IContextualMenuSubComponentStyles; + title: IStyle; +} + +// @public (undocumented) +export interface IContextualMenuSubComponentStyles { + callout: IStyleFunctionOrObject; + menuItem: IStyleFunctionOrObject; +} + // @public (undocumented) export interface ICustomizerProps { contextTransform?: (context: Readonly) => ICustomizerContext; @@ -849,6 +1154,17 @@ export interface IMaskedTextFieldState { maskCursorPosition?: number; } +// @public (undocumented) +export interface IMenuItemStyles extends IButtonStyles { + anchorLink: IStyle; + checkmarkIcon: IStyle; + divider: IStyle; + iconColor: IStyle; + item: IStyle; + linkContent: IStyle; + subMenuIcon: IStyle; +} + // @public (undocumented) export interface IModal { focus: () => void; @@ -926,7 +1242,7 @@ export interface IOverflowSetProps extends React.ClassAttributes any[] | undefined; keytipSequences?: string[]; @@ -1120,9 +1436,9 @@ export interface IPositioningContainerProps extends IBaseProps; coverTarget?: boolean; - directionalHint?: DirectionalHint; + directionalHint?: DirectionalHint_2; directionalHintFixed?: boolean; - directionalHintForRTL?: DirectionalHint; + directionalHintForRTL?: DirectionalHint_2; doNotLayer?: boolean; finalHeight?: number; minPagePadding?: number; @@ -1361,7 +1677,7 @@ export interface ISpinButtonProps extends React.HTMLAttributes { decrementButtonIcon?: IIconProps; defaultValue?: string; disabled?: boolean; - downArrowButtonStyles?: Partial; + downArrowButtonStyles?: Partial; iconButtonProps?: IButtonProps_2; iconProps?: IIconProps; incrementButtonAriaLabel?: string; @@ -1382,7 +1698,7 @@ export interface ISpinButtonProps extends React.HTMLAttributes { styles?: IStyleFunctionOrObject; theme?: ITheme; title?: string; - upArrowButtonStyles?: Partial; + upArrowButtonStyles?: Partial; value?: string; } @@ -2051,7 +2367,6 @@ export * from "office-ui-fabric-react/lib/Color"; export * from "office-ui-fabric-react/lib/ColorPicker"; export * from "office-ui-fabric-react/lib/ComboBox"; export * from "office-ui-fabric-react/lib/CommandBar"; -export * from "office-ui-fabric-react/lib/ContextualMenu"; export * from "office-ui-fabric-react/lib/DetailsList"; export * from "office-ui-fabric-react/lib/Dialog"; export * from "office-ui-fabric-react/lib/Divider"; diff --git a/packages/react-next/src/ContextualMenu.ts b/packages/react-next/src/ContextualMenu.ts index bc523a7109ee5..0e2e266ca80d1 100644 --- a/packages/react-next/src/ContextualMenu.ts +++ b/packages/react-next/src/ContextualMenu.ts @@ -1 +1 @@ -export * from 'office-ui-fabric-react/lib/ContextualMenu'; +export * from './components/ContextualMenu'; diff --git a/packages/react-next/src/common/ThemingSass.scss b/packages/react-next/src/common/ThemingSass.scss new file mode 100644 index 0000000000000..00ee5e3fe3f52 --- /dev/null +++ b/packages/react-next/src/common/ThemingSass.scss @@ -0,0 +1,6 @@ +/** Imports all theming-related SASS files. */ +@import './effects'; +@import './legacyThemePalette'; +@import './semanticSlots'; +@import './themeOverrides'; +@import './themeVariables'; diff --git a/packages/react-next/src/common/_common.scss b/packages/react-next/src/common/_common.scss new file mode 100644 index 0000000000000..325b29dfa1eb3 --- /dev/null +++ b/packages/react-next/src/common/_common.scss @@ -0,0 +1,10 @@ +@import '~office-ui-fabric-core/dist/sass/References'; +@import './i18n'; +@import './themeOverrides'; +@import './focusBorder'; +@import './semanticSlots'; +@import './highContrast'; + +// Screen sizes that align with UHF breakpoints for the website +$uhf-screen-min-mobile: 768px; +$uhf-screen-max-mobile: ($uhf-screen-min-mobile - 1); diff --git a/packages/react-next/src/common/_effects.scss b/packages/react-next/src/common/_effects.scss new file mode 100644 index 0000000000000..b42812353eae8 --- /dev/null +++ b/packages/react-next/src/common/_effects.scss @@ -0,0 +1,8 @@ +$elevation4: '[theme:elevation4, default: 0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)]'; +$elevation8: '[theme:elevation8, default: 0 3.2px 7.2px 0 rgba(0, 0, 0, 0.132), 0 0.6px 1.8px 0 rgba(0, 0, 0, 0.108)]'; +$elevation16: '[theme:elevation16, default: 0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)]'; +$elevation64: '[theme:elevation64, default: 0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)]'; + +$roundedCorner2: '[theme:roundedCorner2, default: 2px]'; +$roundedCorner4: '[theme:roundedCorner4, default: 4px]'; +$roundedCorner6: '[theme:roundedCorner6, default: 6px]'; diff --git a/packages/react-next/src/common/_focusBorder.scss b/packages/react-next/src/common/_focusBorder.scss new file mode 100644 index 0000000000000..3d6e2a5baa273 --- /dev/null +++ b/packages/react-next/src/common/_focusBorder.scss @@ -0,0 +1,63 @@ +@import './semanticSlots'; + +@mixin focus-clear() { + &::-moz-focus-inner { + // Clear the focus border in Firefox. Reference: http://stackoverflow.com/a/199319/1436671 + border: 0; + } + + & { + // Clear browser specific focus styles and use transparent as placeholder for focus style + outline: transparent; + } +} + +@mixin focus($onFocus: true) { + @if $onFocus { + :global(.ms-Fabric--isFocusVisible) &:focus { + @content; + } + } + @else { + @content; + } +} + +@mixin focus-border($padding: 0, $color: $focusBorderColor, $thickness: 1px, $onFocus: true, $position: relative) { + @include focus-clear(); + + & { + // It is MUST because the pseudo-element is absolute position. + position: $position; + } + + @include focus($onFocus) { + &:after { + @include after-outline($padding, $color, $thickness); + } + } +} + +// When focus is set using the keyboard, apply an outline. +@mixin after-outline ($padding: 0, $color: $focusBorderColor, $thickness: 1px) { + content: ''; + position: absolute; + top: $padding; + right: $padding; + bottom: $padding; + left: $padding; + + // Make the content not respond to mouse/touch event. Reference: https://css-tricks.com/almanac/properties/p/pointer-events/ + pointer-events: none; + + // Add focus border with $color + border: $thickness solid $color; +} + + +// When focus is set using the keyboard, apply an outline. +@mixin focus-outline { + :global(.ms-Fabric--isFocusVisible) &:focus { + outline: 1px solid $focusBorderColor; + } +} diff --git a/packages/react-next/src/common/_highContrast.scss b/packages/react-next/src/common/_highContrast.scss new file mode 100644 index 0000000000000..833eb712a288e --- /dev/null +++ b/packages/react-next/src/common/_highContrast.scss @@ -0,0 +1,17 @@ +@mixin high-contrast { + @media screen and (-ms-high-contrast: active) { + @content; + } +} + +@mixin high-contrast-black-on-white { + @media screen and (-ms-high-contrast: black-on-white) { + @content; + } +} + +@mixin high-contrast-white-on-black { + @media screen and (-ms-high-contrast: white-on-black) { + @content; + } +} \ No newline at end of file diff --git a/packages/react-next/src/common/_i18n.scss b/packages/react-next/src/common/_i18n.scss new file mode 100644 index 0000000000000..e7c5230f933bd --- /dev/null +++ b/packages/react-next/src/common/_i18n.scss @@ -0,0 +1,295 @@ +// Some things copied from WinJS with <3 + + +// LTR mixin definition +@mixin LTR { + html[dir='ltr'] & { + @content; + } +} + +// RTL mixin definition +@mixin RTL { + html[dir='rtl'] & { + @content; + } +} + +// Use baseRTL for a root element of a control that needs rtl support +@mixin baseRtl { + @include RTL { + direction: rtl; + unicode-bidi: bidi-override; + } +} + +/* + Common CSS property mixins with support for RTL. + Use these mixins when you want to automatically create RTL versions of your properties. + They are in alphabetical order (a-z). +*/ + +@mixin border-color($top, $right, $bottom, $left) { + border-color: $top $right $bottom $left; + @include RTL { + border-color: $top $left $bottom $right; + } +} + +@mixin border-left($width, $style, $color) { + @include LTR { + border-left: $width $style $color; + } + @include RTL { + border-right: $width $style $color; + } +} + +@mixin border-left-color($color) { + @include LTR { + border-left-color: $color; + } + @include RTL { + border-right-color: $color; + } +} + +@mixin border-left-style($style) { + @include LTR { + border-left-style: $style; + } + @include RTL { + border-right-style: $style; + } +} + +@mixin border-left-width($width) { + @include LTR { + border-left-width: $width; + } + @include RTL { + border-right-width: $width; + } +} + +@mixin border-radius($topLeft, $topRight, $bottomRight, $bottomLeft) { + border-radius: $topLeft $topRight $bottomRight $bottomLeft; + @include RTL { + border-radius: $topRight $topLeft $bottomLeft $bottomRight; + } +} + +@mixin border-right($width, $style, $color) { + @include LTR { + border-right: $width $style $color; + } + @include RTL { + border-left: $width $style $color; + } +} + +@mixin border-right-color($color) { + @include LTR { + border-right-color: $color; + } + @include RTL { + border-left-color: $color; + } +} + +@mixin border-right-style($style) { + @include LTR { + border-right-style: $style; + } + @include RTL { + border-left-style: $style; + } +} + +@mixin border-right-width($width) { + @include LTR { + border-right-width: $width; + } + @include RTL { + border-left-width: $width; + } +} + +@mixin clear($side) { + @if $side == left { + @include LTR { + clear: $side; + } + @include RTL { + clear: right; + } + } @else if $side == right { + @include LTR { + clear: $side; + } + + @include RTL { + clear: left; + } + } @else { + clear: $side; + } +} + +@mixin float($direction) { + @if $direction == left { + @include LTR { + float: left; + } + @include RTL { + float: right; + } + } @else { + @include LTR { + float: right; + } + @include RTL { + float: left; + } + } +} + +@mixin left($distance) { + @include LTR { + left: $distance; + } + @include RTL { + right: $distance; + } +} + +@mixin margin($top, $right, $bottom, $left) { + margin: $top $right $bottom $left; + @include RTL { + margin: $top $left $bottom $right; + } +} + +@mixin margin-left($distance) { + @include LTR { + margin-left: $distance; + } + @include RTL { + margin-right: $distance; + } +} + +@mixin margin-right($distance) { + @include LTR { + margin-right: $distance; + } + @include RTL { + margin-left: $distance; + } +} + +@mixin padding($top, $right, $bottom, $left) { + padding: $top $right $bottom $left; + @include RTL { + padding: $top $left $bottom $right; + } +} + +@mixin padding-left($distance) { + @include LTR { + padding-left: $distance; + } + @include RTL { + padding-right: $distance; + } +} + +@mixin padding-right($distance) { + @include LTR { + padding-right: $distance; + } + @include RTL { + padding-left: $distance; + } +} + +@mixin right($distance) { + @include LTR { + right: $distance; + } + @include RTL { + left: $distance; + } +} + +@mixin text-align($direction) { + @if $direction == left { + @include LTR { + text-align: left; + } + @include RTL { + text-align: right; + } + } @else { + @include LTR { + text-align: right; + } + @include RTL { + text-align: left; + } + } +} + +@mixin box-shadow($left, $etc) { + @include LTR { + box-shadow: $left $etc; + } + + @include RTL { + box-shadow: -$left $etc; + } +} + +@mixin transform-scaleX($scaleX: 1) { + @include LTR { + transform: scaleX($scaleX); + } + @include RTL { + transform: scaleX(-$scaleX); + } +} + +@mixin transform-translateX($distance) { + @include LTR { + transform: translateX($distance); + } + @include RTL { + transform: translateX(-$distance); + } +} + +// only supported when ONLY left/right are declared +@mixin transition-property($direction) { + @if $direction == left { + @include LTR { + transition-property: left; + } + @include RTL { + transition-property: right; + } + } @else { + @include LTR { + transition-property: right; + } + @include RTL { + transition-property: left; + } + } +} + +// Disables high contrast color adjusts for Edge/IE11 +@mixin highContrastAdjust { + @media screen and (-ms-high-contrast: active), screen and (-ms-high-contrast: black-on-white) { + -ms-high-contrast-adjust: none; + } +} \ No newline at end of file diff --git a/packages/react-next/src/common/_legacyThemePalette.scss b/packages/react-next/src/common/_legacyThemePalette.scss new file mode 100644 index 0000000000000..003bc3bcf5766 --- /dev/null +++ b/packages/react-next/src/common/_legacyThemePalette.scss @@ -0,0 +1,19 @@ +$ms-color-error: "[theme:error, default: #a80000]"; +$ms-color-errorText: "[theme:warningText, default: #333333]"; +$ms-color-errorBackground: "[theme:errorBackground, default: rgba(232,17,35,.2)]"; +$ms-color-success: "[theme:success, default: #107c10]"; +$ms-color-successText: "[theme:successText, default: #333333]"; +$ms-color-successBackground: "[theme:successBackground, default: #dff6dd]"; +$ms-color-alert: "[theme:alert, default: #d83b01]"; +$ms-color-alertText: "[theme:alertText, default: #333333]"; +$ms-color-alertBackground: "[theme:alertBackground, default: #deecf9]"; +$ms-color-warning: "[theme:warning, default: #767676]"; +$ms-color-warningText: "[theme:warningText, default: #333333]"; +$ms-color-warningBackground: "[theme:warningBackground, default: #fff4ce]"; +$ms-color-severeWarning: "[theme:severeWarning, default: #a80000]"; +$ms-color-severeWarningText: "[theme:severeWarningText, default: #333333]"; +$ms-color-severeWarningBackground: "[theme:severeWarningBackground, default: #fed9cc]"; +$ms-color-info: "[theme:info, default: #666666]"; +$ms-color-infoText: "[theme:infoText, default: #333333]"; +$ms-color-infoBackground: "[theme:infoBackground, default: #f4f4f4]"; +$ms-color-themeAccent: "[theme:accent, default:#0078d4]"; \ No newline at end of file diff --git a/packages/react-next/src/common/_semanticSlots.scss b/packages/react-next/src/common/_semanticSlots.scss new file mode 100644 index 0000000000000..3341c5c1f7c80 --- /dev/null +++ b/packages/react-next/src/common/_semanticSlots.scss @@ -0,0 +1,101 @@ +/** THIS FILE IS AUTOGENERATED do not modify it manually. See generateDefaultThemeSassFiles.js. New slots should be added to the appropriate interfaces and defaults files. */ +$bodyBackgroundColor: '[theme:bodyBackground, default: #ffffff]'; +$bodyBackgroundHoveredColor: '[theme:bodyBackgroundHovered, default: #f3f2f1]'; +$bodyBackgroundCheckedColor: '[theme:bodyBackgroundChecked, default: #edebe9]'; +$bodyStandoutBackgroundColor: '[theme:bodyStandoutBackground, default: #faf9f8]'; +$bodyFrameBackgroundColor: '[theme:bodyFrameBackground, default: #ffffff]'; +$bodyFrameDividerColor: '[theme:bodyFrameDivider, default: #edebe9]'; +$bodyTextColor: '[theme:bodyText, default: #323130]'; +$bodyTextCheckedColor: '[theme:bodyTextChecked, default: #000000]'; +$bodySubtextColor: '[theme:bodySubtext, default: #605e5c]'; +$bodyDividerColor: '[theme:bodyDivider, default: #edebe9]'; +$disabledBodyTextColor: '[theme:disabledBodyText, default: #a19f9d]'; +$disabledBodySubtextColor: '[theme:disabledBodySubtext, default: #c8c6c4]'; +$disabledBorderColor: '[theme:disabledBorder, default: #c8c6c4]'; +$focusBorderColor: '[theme:focusBorder, default: #605e5c]'; +$variantBorderColor: '[theme:variantBorder, default: #edebe9]'; +$variantBorderHoveredColor: '[theme:variantBorderHovered, default: #a19f9d]'; +$defaultStateBackgroundColor: '[theme:defaultStateBackground, default: #faf9f8]'; +$actionLinkColor: '[theme:actionLink, default: #323130]'; +$actionLinkHoveredColor: '[theme:actionLinkHovered, default: #201f1e]'; +$linkColor: '[theme:link, default: #0078d4]'; +$linkHoveredColor: '[theme:linkHovered, default: #004578]'; +$buttonBackgroundColor: '[theme:buttonBackground, default: #ffffff]'; +$buttonBackgroundCheckedColor: '[theme:buttonBackgroundChecked, default: #c8c6c4]'; +$buttonBackgroundHoveredColor: '[theme:buttonBackgroundHovered, default: #f3f2f1]'; +$buttonBackgroundCheckedHoveredColor: '[theme:buttonBackgroundCheckedHovered, default: #edebe9]'; +$buttonBackgroundPressedColor: '[theme:buttonBackgroundPressed, default: #edebe9]'; +$buttonBackgroundDisabledColor: '[theme:buttonBackgroundDisabled, default: #f3f2f1]'; +$buttonBorderColor: '[theme:buttonBorder, default: #8a8886]'; +$buttonTextColor: '[theme:buttonText, default: #323130]'; +$buttonTextHoveredColor: '[theme:buttonTextHovered, default: #201f1e]'; +$buttonTextCheckedColor: '[theme:buttonTextChecked, default: #201f1e]'; +$buttonTextCheckedHoveredColor: '[theme:buttonTextCheckedHovered, default: #000000]'; +$buttonTextPressedColor: '[theme:buttonTextPressed, default: #201f1e]'; +$buttonTextDisabledColor: '[theme:buttonTextDisabled, default: #a19f9d]'; +$buttonBorderDisabledColor: '[theme:buttonBorderDisabled, default: #f3f2f1]'; +$primaryButtonBackgroundColor: '[theme:primaryButtonBackground, default: #0078d4]'; +$primaryButtonBackgroundHoveredColor: '[theme:primaryButtonBackgroundHovered, default: #106ebe]'; +$primaryButtonBackgroundPressedColor: '[theme:primaryButtonBackgroundPressed, default: #005a9e]'; +$primaryButtonBackgroundDisabledColor: '[theme:primaryButtonBackgroundDisabled, default: #f3f2f1]'; +$primaryButtonBorderColor: '[theme:primaryButtonBorder, default: transparent]'; +$primaryButtonTextColor: '[theme:primaryButtonText, default: #ffffff]'; +$primaryButtonTextHoveredColor: '[theme:primaryButtonTextHovered, default: #ffffff]'; +$primaryButtonTextPressedColor: '[theme:primaryButtonTextPressed, default: #ffffff]'; +$primaryButtonTextDisabledColor: '[theme:primaryButtonTextDisabled, default: #d2d0ce]'; +$accentButtonBackgroundColor: '[theme:accentButtonBackground, default: #0078d4]'; +$accentButtonTextColor: '[theme:accentButtonText, default: #ffffff]'; +$inputBorderColor: '[theme:inputBorder, default: #605e5c]'; +$inputBorderHoveredColor: '[theme:inputBorderHovered, default: #323130]'; +$inputBackgroundColor: '[theme:inputBackground, default: #ffffff]'; +$inputBackgroundCheckedColor: '[theme:inputBackgroundChecked, default: #0078d4]'; +$inputBackgroundCheckedHoveredColor: '[theme:inputBackgroundCheckedHovered, default: #005a9e]'; +$inputPlaceholderBackgroundCheckedColor: '[theme:inputPlaceholderBackgroundChecked, default: #deecf9]'; +$inputForegroundCheckedColor: '[theme:inputForegroundChecked, default: #ffffff]'; +$inputIconColor: '[theme:inputIcon, default: #0078d4]'; +$inputIconHoveredColor: '[theme:inputIconHovered, default: #005a9e]'; +$inputIconDisabledColor: '[theme:inputIconDisabled, default: #a19f9d]'; +$inputFocusBorderAltColor: '[theme:inputFocusBorderAlt, default: #0078d4]'; +$smallInputBorderColor: '[theme:smallInputBorder, default: #605e5c]'; +$inputTextColor: '[theme:inputText, default: #323130]'; +$inputTextHoveredColor: '[theme:inputTextHovered, default: #201f1e]'; +$inputPlaceholderTextColor: '[theme:inputPlaceholderText, default: #605e5c]'; +$disabledBackgroundColor: '[theme:disabledBackground, default: #f3f2f1]'; +$disabledTextColor: '[theme:disabledText, default: #a19f9d]'; +$disabledSubtextColor: '[theme:disabledSubtext, default: #d2d0ce]'; +$listBackgroundColor: '[theme:listBackground, default: #ffffff]'; +$listTextColor: '[theme:listText, default: #323130]'; +$listItemBackgroundHoveredColor: '[theme:listItemBackgroundHovered, default: #f3f2f1]'; +$listItemBackgroundCheckedColor: '[theme:listItemBackgroundChecked, default: #edebe9]'; +$listItemBackgroundCheckedHoveredColor: '[theme:listItemBackgroundCheckedHovered, default: #e1dfdd]'; +$listHeaderBackgroundHoveredColor: '[theme:listHeaderBackgroundHovered, default: #f3f2f1]'; +$listHeaderBackgroundPressedColor: '[theme:listHeaderBackgroundPressed, default: #edebe9]'; +$menuBackgroundColor: '[theme:menuBackground, default: #ffffff]'; +$menuDividerColor: '[theme:menuDivider, default: #c8c6c4]'; +$menuIconColor: '[theme:menuIcon, default: #0078d4]'; +$menuHeaderColor: '[theme:menuHeader, default: #0078d4]'; +$menuItemBackgroundHoveredColor: '[theme:menuItemBackgroundHovered, default: #f3f2f1]'; +$menuItemBackgroundPressedColor: '[theme:menuItemBackgroundPressed, default: #edebe9]'; +$menuItemTextColor: '[theme:menuItemText, default: #323130]'; +$menuItemTextHoveredColor: '[theme:menuItemTextHovered, default: #201f1e]'; +$errorTextColor: '[theme:errorText, default: #a4262c]'; +$messageTextColor: '[theme:messageText, default: #323130]'; +$messageLinkColor: '[theme:messageLink, default: #005A9E]'; +$messageLinkHoveredColor: '[theme:messageLinkHovered, default: #004578]'; +$infoIconColor: '[theme:infoIcon, default: #605e5c]'; +$errorIconColor: '[theme:errorIcon, default: #A80000]'; +$blockingIconColor: '[theme:blockingIcon, default: #FDE7E9]'; +$warningIconColor: '[theme:warningIcon, default: #797775]'; +$severeWarningIconColor: '[theme:severeWarningIcon, default: #D83B01]'; +$successIconColor: '[theme:successIcon, default: #107C10]'; +$infoBackgroundColor: '[theme:infoBackground, default: #f3f2f1]'; +$errorBackgroundColor: '[theme:errorBackground, default: #FDE7E9]'; +$blockingBackgroundColor: '[theme:blockingBackground, default: #FDE7E9]'; +$warningBackgroundColor: '[theme:warningBackground, default: #FFF4CE]'; +$severeWarningBackgroundColor: '[theme:severeWarningBackground, default: #FED9CC]'; +$successBackgroundColor: '[theme:successBackground, default: #DFF6DD]'; +$warningHighlightColor: '[theme:warningHighlight, default: #ffb900]'; /* @deprecated */ +$warningTextColor: '[theme:warningText, default: #323130]'; /* @deprecated */ +$successTextColor: '[theme:successText, default: #107C10]'; /* @deprecated */ +$listTextColorColor: '[theme:listTextColor, default: #323130]'; /* @deprecated */ +$menuItemBackgroundCheckedColor: '[theme:menuItemBackgroundChecked, default: #edebe9]'; /* @deprecated */ \ No newline at end of file diff --git a/packages/react-next/src/common/_themeOverrides.scss b/packages/react-next/src/common/_themeOverrides.scss new file mode 100644 index 0000000000000..5f46f913fdbce --- /dev/null +++ b/packages/react-next/src/common/_themeOverrides.scss @@ -0,0 +1,51 @@ +/** THIS FILE IS AUTOGENERATED do not modify it manually. See generateDefaultThemeSassFiles.js. New slots should be added to the appropriate interfaces and defaults files. */ +$ms-color-themeDarker: "[theme:themeDarker, default: #004578]"; +$ms-color-themeDark: "[theme:themeDark, default: #005a9e]"; +$ms-color-themeDarkAlt: "[theme:themeDarkAlt, default: #106ebe]"; +$ms-color-themePrimary: "[theme:themePrimary, default: #0078d4]"; +$ms-color-themeSecondary: "[theme:themeSecondary, default: #2b88d8]"; +$ms-color-themeTertiary: "[theme:themeTertiary, default: #71afe5]"; +$ms-color-themeLight: "[theme:themeLight, default: #c7e0f4]"; +$ms-color-themeLighter: "[theme:themeLighter, default: #deecf9]"; +$ms-color-themeLighterAlt: "[theme:themeLighterAlt, default: #eff6fc]"; +$ms-color-black: "[theme:black, default: #000000]"; +$ms-color-blackTranslucent40: "[theme:blackTranslucent40, default: rgba(0,0,0,.4)]"; +$ms-color-neutralDark: "[theme:neutralDark, default: #201f1e]"; +$ms-color-neutralPrimary: "[theme:neutralPrimary, default: #323130]"; +$ms-color-neutralPrimaryAlt: "[theme:neutralPrimaryAlt, default: #3b3a39]"; +$ms-color-neutralSecondary: "[theme:neutralSecondary, default: #605e5c]"; +$ms-color-neutralSecondaryAlt: "[theme:neutralSecondaryAlt, default: #8a8886]"; +$ms-color-neutralTertiary: "[theme:neutralTertiary, default: #a19f9d]"; +$ms-color-neutralTertiaryAlt: "[theme:neutralTertiaryAlt, default: #c8c6c4]"; +$ms-color-neutralQuaternary: "[theme:neutralQuaternary, default: #d2d0ce]"; +$ms-color-neutralQuaternaryAlt: "[theme:neutralQuaternaryAlt, default: #e1dfdd]"; +$ms-color-neutralLight: "[theme:neutralLight, default: #edebe9]"; +$ms-color-neutralLighter: "[theme:neutralLighter, default: #f3f2f1]"; +$ms-color-neutralLighterAlt: "[theme:neutralLighterAlt, default: #faf9f8]"; +$ms-color-accent: "[theme:accent, default: #0078d4]"; +$ms-color-white: "[theme:white, default: #ffffff]"; +$ms-color-whiteTranslucent40: "[theme:whiteTranslucent40, default: rgba(255,255,255,.4)]"; +$ms-color-yellowDark: "[theme:yellowDark, default: #d29200]"; +$ms-color-yellow: "[theme:yellow, default: #ffb900]"; +$ms-color-yellowLight: "[theme:yellowLight, default: #fff100]"; +$ms-color-orange: "[theme:orange, default: #d83b01]"; +$ms-color-orangeLight: "[theme:orangeLight, default: #ea4300]"; +$ms-color-orangeLighter: "[theme:orangeLighter, default: #ff8c00]"; +$ms-color-redDark: "[theme:redDark, default: #a4262c]"; +$ms-color-red: "[theme:red, default: #e81123]"; +$ms-color-magentaDark: "[theme:magentaDark, default: #5c005c]"; +$ms-color-magenta: "[theme:magenta, default: #b4009e]"; +$ms-color-magentaLight: "[theme:magentaLight, default: #e3008c]"; +$ms-color-purpleDark: "[theme:purpleDark, default: #32145a]"; +$ms-color-purple: "[theme:purple, default: #5c2d91]"; +$ms-color-purpleLight: "[theme:purpleLight, default: #b4a0ff]"; +$ms-color-blueDark: "[theme:blueDark, default: #002050]"; +$ms-color-blueMid: "[theme:blueMid, default: #00188f]"; +$ms-color-blue: "[theme:blue, default: #0078d4]"; +$ms-color-blueLight: "[theme:blueLight, default: #00bcf2]"; +$ms-color-tealDark: "[theme:tealDark, default: #004b50]"; +$ms-color-teal: "[theme:teal, default: #008272]"; +$ms-color-tealLight: "[theme:tealLight, default: #00b294]"; +$ms-color-greenDark: "[theme:greenDark, default: #004b1c]"; +$ms-color-green: "[theme:green, default: #107c10]"; +$ms-color-greenLight: "[theme:greenLight, default: #bad80a]"; \ No newline at end of file diff --git a/packages/react-next/src/common/_themeVariables.scss b/packages/react-next/src/common/_themeVariables.scss new file mode 100644 index 0000000000000..2827cf95bc789 --- /dev/null +++ b/packages/react-next/src/common/_themeVariables.scss @@ -0,0 +1,118 @@ +/** THIS FILE IS AUTOGENERATED do not modify it manually. See generateDefaultThemeSassFiles.js. New slots should be added to the appropriate interfaces and defaults files. */ +$ms-font-tinyFontFamily: "[theme:tinyFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; +$ms-font-tinyMozOsxFontSmoothing: "[theme:tinyMozOsxFontSmoothing, default: grayscale]"; +$ms-font-tinyWebkitFontSmoothing: "[theme:tinyWebkitFontSmoothing, default: antialiased]"; +$ms-font-tinyFontSize: "[theme:tinyFontSize, default: 10px]"; +$ms-font-tinyFontWeight: "[theme:tinyFontWeight, default: 400]"; +@mixin tinyFontBasic { + font-size: $ms-font-tinyFontSize; + font-weight: $ms-font-tinyFontWeight; +} +$ms-font-xSmallFontFamily: "[theme:xSmallFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; +$ms-font-xSmallMozOsxFontSmoothing: "[theme:xSmallMozOsxFontSmoothing, default: grayscale]"; +$ms-font-xSmallWebkitFontSmoothing: "[theme:xSmallWebkitFontSmoothing, default: antialiased]"; +$ms-font-xSmallFontSize: "[theme:xSmallFontSize, default: 10px]"; +$ms-font-xSmallFontWeight: "[theme:xSmallFontWeight, default: 400]"; +@mixin xSmallFontBasic { + font-size: $ms-font-xSmallFontSize; + font-weight: $ms-font-xSmallFontWeight; +} +$ms-font-smallFontFamily: "[theme:smallFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; +$ms-font-smallMozOsxFontSmoothing: "[theme:smallMozOsxFontSmoothing, default: grayscale]"; +$ms-font-smallWebkitFontSmoothing: "[theme:smallWebkitFontSmoothing, default: antialiased]"; +$ms-font-smallFontSize: "[theme:smallFontSize, default: 12px]"; +$ms-font-smallFontWeight: "[theme:smallFontWeight, default: 400]"; +@mixin smallFontBasic { + font-size: $ms-font-smallFontSize; + font-weight: $ms-font-smallFontWeight; +} +$ms-font-smallPlusFontFamily: "[theme:smallPlusFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; +$ms-font-smallPlusMozOsxFontSmoothing: "[theme:smallPlusMozOsxFontSmoothing, default: grayscale]"; +$ms-font-smallPlusWebkitFontSmoothing: "[theme:smallPlusWebkitFontSmoothing, default: antialiased]"; +$ms-font-smallPlusFontSize: "[theme:smallPlusFontSize, default: 12px]"; +$ms-font-smallPlusFontWeight: "[theme:smallPlusFontWeight, default: 400]"; +@mixin smallPlusFontBasic { + font-size: $ms-font-smallPlusFontSize; + font-weight: $ms-font-smallPlusFontWeight; +} +$ms-font-mediumFontFamily: "[theme:mediumFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; +$ms-font-mediumMozOsxFontSmoothing: "[theme:mediumMozOsxFontSmoothing, default: grayscale]"; +$ms-font-mediumWebkitFontSmoothing: "[theme:mediumWebkitFontSmoothing, default: antialiased]"; +$ms-font-mediumFontSize: "[theme:mediumFontSize, default: 14px]"; +$ms-font-mediumFontWeight: "[theme:mediumFontWeight, default: 400]"; +@mixin mediumFontBasic { + font-size: $ms-font-mediumFontSize; + font-weight: $ms-font-mediumFontWeight; +} +$ms-font-mediumPlusFontFamily: "[theme:mediumPlusFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; +$ms-font-mediumPlusMozOsxFontSmoothing: "[theme:mediumPlusMozOsxFontSmoothing, default: grayscale]"; +$ms-font-mediumPlusWebkitFontSmoothing: "[theme:mediumPlusWebkitFontSmoothing, default: antialiased]"; +$ms-font-mediumPlusFontSize: "[theme:mediumPlusFontSize, default: 16px]"; +$ms-font-mediumPlusFontWeight: "[theme:mediumPlusFontWeight, default: 400]"; +@mixin mediumPlusFontBasic { + font-size: $ms-font-mediumPlusFontSize; + font-weight: $ms-font-mediumPlusFontWeight; +} +$ms-font-largeFontFamily: "[theme:largeFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; +$ms-font-largeMozOsxFontSmoothing: "[theme:largeMozOsxFontSmoothing, default: grayscale]"; +$ms-font-largeWebkitFontSmoothing: "[theme:largeWebkitFontSmoothing, default: antialiased]"; +$ms-font-largeFontSize: "[theme:largeFontSize, default: 18px]"; +$ms-font-largeFontWeight: "[theme:largeFontWeight, default: 400]"; +@mixin largeFontBasic { + font-size: $ms-font-largeFontSize; + font-weight: $ms-font-largeFontWeight; +} +$ms-font-xLargeFontFamily: "[theme:xLargeFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; +$ms-font-xLargeMozOsxFontSmoothing: "[theme:xLargeMozOsxFontSmoothing, default: grayscale]"; +$ms-font-xLargeWebkitFontSmoothing: "[theme:xLargeWebkitFontSmoothing, default: antialiased]"; +$ms-font-xLargeFontSize: "[theme:xLargeFontSize, default: 20px]"; +$ms-font-xLargeFontWeight: "[theme:xLargeFontWeight, default: 600]"; +@mixin xLargeFontBasic { + font-size: $ms-font-xLargeFontSize; + font-weight: $ms-font-xLargeFontWeight; +} +$ms-font-xLargePlusFontFamily: "[theme:xLargePlusFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; +$ms-font-xLargePlusMozOsxFontSmoothing: "[theme:xLargePlusMozOsxFontSmoothing, default: grayscale]"; +$ms-font-xLargePlusWebkitFontSmoothing: "[theme:xLargePlusWebkitFontSmoothing, default: antialiased]"; +$ms-font-xLargePlusFontSize: "[theme:xLargePlusFontSize, default: 24px]"; +$ms-font-xLargePlusFontWeight: "[theme:xLargePlusFontWeight, default: 600]"; +@mixin xLargePlusFontBasic { + font-size: $ms-font-xLargePlusFontSize; + font-weight: $ms-font-xLargePlusFontWeight; +} +$ms-font-xxLargeFontFamily: "[theme:xxLargeFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; +$ms-font-xxLargeMozOsxFontSmoothing: "[theme:xxLargeMozOsxFontSmoothing, default: grayscale]"; +$ms-font-xxLargeWebkitFontSmoothing: "[theme:xxLargeWebkitFontSmoothing, default: antialiased]"; +$ms-font-xxLargeFontSize: "[theme:xxLargeFontSize, default: 28px]"; +$ms-font-xxLargeFontWeight: "[theme:xxLargeFontWeight, default: 600]"; +@mixin xxLargeFontBasic { + font-size: $ms-font-xxLargeFontSize; + font-weight: $ms-font-xxLargeFontWeight; +} +$ms-font-xxLargePlusFontFamily: "[theme:xxLargePlusFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; +$ms-font-xxLargePlusMozOsxFontSmoothing: "[theme:xxLargePlusMozOsxFontSmoothing, default: grayscale]"; +$ms-font-xxLargePlusWebkitFontSmoothing: "[theme:xxLargePlusWebkitFontSmoothing, default: antialiased]"; +$ms-font-xxLargePlusFontSize: "[theme:xxLargePlusFontSize, default: 32px]"; +$ms-font-xxLargePlusFontWeight: "[theme:xxLargePlusFontWeight, default: 600]"; +@mixin xxLargePlusFontBasic { + font-size: $ms-font-xxLargePlusFontSize; + font-weight: $ms-font-xxLargePlusFontWeight; +} +$ms-font-superLargeFontFamily: "[theme:superLargeFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; +$ms-font-superLargeMozOsxFontSmoothing: "[theme:superLargeMozOsxFontSmoothing, default: grayscale]"; +$ms-font-superLargeWebkitFontSmoothing: "[theme:superLargeWebkitFontSmoothing, default: antialiased]"; +$ms-font-superLargeFontSize: "[theme:superLargeFontSize, default: 42px]"; +$ms-font-superLargeFontWeight: "[theme:superLargeFontWeight, default: 600]"; +@mixin superLargeFontBasic { + font-size: $ms-font-superLargeFontSize; + font-weight: $ms-font-superLargeFontWeight; +} +$ms-font-megaFontFamily: "[theme:megaFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; +$ms-font-megaMozOsxFontSmoothing: "[theme:megaMozOsxFontSmoothing, default: grayscale]"; +$ms-font-megaWebkitFontSmoothing: "[theme:megaWebkitFontSmoothing, default: antialiased]"; +$ms-font-megaFontSize: "[theme:megaFontSize, default: 68px]"; +$ms-font-megaFontWeight: "[theme:megaFontWeight, default: 600]"; +@mixin megaFontBasic { + font-size: $ms-font-megaFontSize; + font-weight: $ms-font-megaFontWeight; +} \ No newline at end of file diff --git a/packages/react-next/src/common/sharedIsConformant.ts b/packages/react-next/src/common/sharedIsConformant.ts index 203956af81a69..87164d38f24e9 100644 --- a/packages/react-next/src/common/sharedIsConformant.ts +++ b/packages/react-next/src/common/sharedIsConformant.ts @@ -3,7 +3,7 @@ import { isConformant, IsConformantOptions } from '@fluentui/react-conformance'; export function sharedIsConformant(testInfo: IsConformantOptions) { const defaultOptions = { - disabledTests: ['has-docblock', 'kebab-aria-attributes'], + disabledTests: ['has-docblock'], }; isConformant(defaultOptions, testInfo); diff --git a/packages/react-next/src/common/testUtilities.ts b/packages/react-next/src/common/testUtilities.ts index c80b031a6f429..81de215e56d85 100644 --- a/packages/react-next/src/common/testUtilities.ts +++ b/packages/react-next/src/common/testUtilities.ts @@ -4,23 +4,26 @@ import { Component } from 'react'; import * as ReactDOM from 'react-dom'; import * as ReactTestUtils from 'react-dom/test-utils'; -/* eslint-disable @typescript-eslint/no-explicit-any */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function findNodes(wrapper: ReactWrapper, className: string): ReactWrapper { + // eslint-disable-next-line @typescript-eslint/no-explicit-any return wrapper.find(className).filterWhere((node: ReactWrapper) => typeof node.type() === 'string'); } +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function expectNodes(wrapper: ReactWrapper, className: string, n: number): void { expect(findNodes(wrapper, className).length).toEqual(n); } +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function expectOne(wrapper: ReactWrapper, className: string): void { expectNodes(wrapper, className, 1); } +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function expectMissing(wrapper: ReactWrapper, className: string): void { expectNodes(wrapper, className, 0); } -/* eslint-enable @typescript-eslint/no-explicit-any */ /** @deprecated Use fake timers and `jest.runAllTimers()` instead */ export function delay(millisecond: number): Promise { diff --git a/packages/react-next/src/components/ContextualMenu/ContextualMenu.base.tsx b/packages/react-next/src/components/ContextualMenu/ContextualMenu.base.tsx new file mode 100644 index 0000000000000..1405a63a66985 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/ContextualMenu.base.tsx @@ -0,0 +1,1382 @@ +import * as React from 'react'; +import { + IContextualMenuProps, + IContextualMenuItem, + ContextualMenuItemType, + IContextualMenuListProps, + IContextualMenuStyleProps, + IContextualMenuStyles, + IContextualMenuItemRenderProps, +} from './ContextualMenu.types'; +import { DirectionalHint } from '../../common/DirectionalHint'; +import { FocusZone, FocusZoneDirection, IFocusZoneProps, FocusZoneTabbableElements } from '../../FocusZone'; +import { IMenuItemClassNames, IContextualMenuClassNames } from './ContextualMenu.classNames'; +import { + divProperties, + getNativeProps, + shallowCompare, + warnDeprecations, + Async, + EventGroup, + assign, + classNamesFunction, + css, + getDocument, + getFirstFocusable, + getId, + getLastFocusable, + getRTL, + getWindow, + IRenderFunction, + Point, + KeyCodes, + shouldWrapFocus, + IStyleFunctionOrObject, + isIOS, + isMac, + initializeComponentRef, + memoizeFunction, +} from '../../Utilities'; +import { hasSubmenu, getIsChecked, isItemDisabled } from '../../utilities/contextualMenu/index'; +import { withResponsiveMode, ResponsiveMode } from 'office-ui-fabric-react/lib/utilities/decorators/withResponsiveMode'; +import { Callout, ICalloutContentStyleProps, ICalloutContentStyles, Target } from '../../Callout'; +import { ContextualMenuItem } from './ContextualMenuItem'; +import { + ContextualMenuSplitButton, + ContextualMenuButton, + ContextualMenuAnchor, +} from './ContextualMenuItemWrapper/index'; +import { IProcessedStyleSet, concatStyleSetsWithProps } from '../../Styling'; +import { IContextualMenuItemStyleProps, IContextualMenuItemStyles } from './ContextualMenuItem.types'; +import { getItemStyles } from './ContextualMenu.classNames'; + +const getClassNames = classNamesFunction(); +const getContextualMenuItemClassNames = classNamesFunction(); + +export interface IContextualMenuState { + expandedMenuItemKey?: string; + /** True if the menu was expanded by mouse click OR hover (as opposed to by keyboard) */ + expandedByMouseClick?: boolean; + dismissedMenuItemKey?: string; + contextualMenuItems?: IContextualMenuItem[]; + contextualMenuTarget?: Element; + submenuTarget?: Element; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + positions?: any; + slideDirectionalClassName?: string; + subMenuId?: string; + submenuDirection?: DirectionalHint; +} + +export function getSubmenuItems(item: IContextualMenuItem): IContextualMenuItem[] | undefined { + return item.subMenuProps ? item.subMenuProps.items : item.items; +} + +/** + * Returns true if a list of menu items can contain a checkbox + */ +export function canAnyMenuItemsCheck(items: IContextualMenuItem[]): boolean { + return items.some(item => { + if (item.canCheck) { + return true; + } + + // If the item is a section, check if any of the items in the section can check. + if (item.sectionProps && item.sectionProps.items.some(submenuItem => submenuItem.canCheck === true)) { + return true; + } + + return false; + }); +} + +const NavigationIdleDelay = 250 /* ms */; + +const COMPONENT_NAME = 'ContextualMenu'; + +const _getMenuItemStylesFunction = memoizeFunction( + ( + ...styles: (IStyleFunctionOrObject | undefined)[] + ): IStyleFunctionOrObject => { + return (styleProps: IContextualMenuItemStyleProps) => + concatStyleSetsWithProps(styleProps, getItemStyles, ...styles); + }, +); + +@withResponsiveMode +export class ContextualMenuBase extends React.Component { + // The default ContextualMenu properties have no items and beak, the default submenu direction is right and top. + public static defaultProps: IContextualMenuProps = { + items: [], + shouldFocusOnMount: true, + gapSpace: 0, + directionalHint: DirectionalHint.bottomAutoEdge, + beakWidth: 16, + }; + + private _async: Async; + private _events: EventGroup; + private _id: string; + private _host: HTMLElement; + private _previousActiveElement: HTMLElement | undefined; + private _enterTimerId: number | undefined; + private _targetWindow: Window; + private _target: Element | MouseEvent | Point | null; + private _isScrollIdle: boolean; + private _scrollIdleTimeoutId: number | undefined; + /** True if the most recent keydown event was for alt (option) or meta (command). */ + private _lastKeyDownWasAltOrMeta: boolean | undefined; + private _shouldUpdateFocusOnMouseEvent: boolean; + private _gotMouseMove: boolean; + private _mounted = false; + private _focusingPreviousElement: boolean; + + private _adjustedFocusZoneProps: IFocusZoneProps; + + // eslint-disable-next-line deprecation/deprecation + private _classNames: IProcessedStyleSet | IContextualMenuClassNames; + + constructor(props: IContextualMenuProps) { + super(props); + + this._async = new Async(this); + this._events = new EventGroup(this); + initializeComponentRef(this); + + warnDeprecations(COMPONENT_NAME, props, { + getMenuClassNames: 'styles', + }); + + this.state = { + contextualMenuItems: undefined, + subMenuId: getId('ContextualMenu'), + }; + + this._id = props.id || getId('ContextualMenu'); + this._focusingPreviousElement = false; + this._isScrollIdle = true; + this._shouldUpdateFocusOnMouseEvent = !this.props.delayUpdateFocusOnHover; + this._gotMouseMove = false; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public dismiss = (ev?: any, dismissAll?: boolean) => { + const { onDismiss } = this.props; + + if (onDismiss) { + onDismiss(ev, dismissAll); + } + }; + + public shouldComponentUpdate(newProps: IContextualMenuProps, newState: IContextualMenuState): boolean { + if (!newProps.shouldUpdateWhenHidden && this.props.hidden && newProps.hidden) { + // Do not update when hidden. + return false; + } + + return !shallowCompare(this.props, newProps) || !shallowCompare(this.state, newState); + } + + public UNSAFE_componentWillUpdate(newProps: IContextualMenuProps): void { + if (newProps.target !== this.props.target) { + const newTarget = newProps.target; + this._setTargetWindowAndElement(newTarget!); + } + + if (this._isHidden(newProps) !== this._isHidden(this.props)) { + if (this._isHidden(newProps)) { + this._onMenuClosed(); + } else { + this._onMenuOpened(); + this._previousActiveElement = this._targetWindow + ? (this._targetWindow.document.activeElement as HTMLElement) + : undefined; + } + } + if (newProps.delayUpdateFocusOnHover !== this.props.delayUpdateFocusOnHover) { + // update shouldUpdateFocusOnMouseEvent to follow what was passed in + this._shouldUpdateFocusOnMouseEvent = !newProps.delayUpdateFocusOnHover; + + // If shouldUpdateFocusOnMouseEvent is false, we need to reset gotMouseMove to false + this._gotMouseMove = this._shouldUpdateFocusOnMouseEvent && this._gotMouseMove; + } + } + + // Invoked once, both on the client and server, immediately before the initial rendering occurs. + public UNSAFE_componentWillMount() { + const target = this.props.target; + this._setTargetWindowAndElement(target!); + if (!this.props.hidden) { + this._previousActiveElement = this._targetWindow + ? (this._targetWindow.document.activeElement as HTMLElement) + : undefined; + } + } + + // Invoked once, only on the client (not on the server), immediately after the initial rendering occurs. + public componentDidMount(): void { + if (!this.props.hidden) { + this._onMenuOpened(); + } + + this._mounted = true; + } + + // Invoked immediately before a component is unmounted from the DOM. + public componentWillUnmount() { + if (this.props.onMenuDismissed) { + this.props.onMenuDismissed(this.props); + } + + this._events.dispose(); + this._async.dispose(); + this._mounted = false; + } + + public render(): JSX.Element | null { + let { isBeakVisible } = this.props; + + const { + items, + labelElementId, + id, + className, + beakWidth, + directionalHint, + directionalHintForRTL, + alignTargetEdge, + gapSpace, + coverTarget, + ariaLabel, + doNotLayer, + target, + bounds, + useTargetWidth, + useTargetAsMinWidth, + directionalHintFixed, + shouldFocusOnMount, + shouldFocusOnContainer, + title, + styles, + theme, + calloutProps, + onRenderSubMenu = this._onRenderSubMenu, + onRenderMenuList = this._onRenderMenuList, + focusZoneProps, + // eslint-disable-next-line deprecation/deprecation + getMenuClassNames, + } = this.props; + + this._classNames = getMenuClassNames + ? getMenuClassNames(theme!, className) + : getClassNames(styles, { + theme: theme!, + className: className, + }); + + const hasIcons = itemsHaveIcons(items); + + function itemsHaveIcons(contextualMenuItems: IContextualMenuItem[]): boolean { + for (const item of contextualMenuItems) { + if (item.iconProps) { + return true; + } + + if ( + item.itemType === ContextualMenuItemType.Section && + item.sectionProps && + itemsHaveIcons(item.sectionProps.items) + ) { + return true; + } + } + + return false; + } + + this._adjustedFocusZoneProps = { ...focusZoneProps, direction: this._getFocusZoneDirection() }; + + const hasCheckmarks = canAnyMenuItemsCheck(items); + const submenuProps = this.state.expandedMenuItemKey && this.props.hidden !== true ? this._getSubmenuProps() : null; + + isBeakVisible = isBeakVisible === undefined ? this.props.responsiveMode! <= ResponsiveMode.medium : isBeakVisible; + /** + * When useTargetWidth is true, get the width of the target element and apply it for the context menu container + */ + let contextMenuStyle; + const targetAsHtmlElement = this._target as HTMLElement; + if ((useTargetWidth || useTargetAsMinWidth) && targetAsHtmlElement && targetAsHtmlElement.offsetWidth) { + const targetBoundingRect = targetAsHtmlElement.getBoundingClientRect(); + const targetWidth = targetBoundingRect.width - 2 /* Accounts for 1px border */; + + if (useTargetWidth) { + contextMenuStyle = { + width: targetWidth, + }; + } else if (useTargetAsMinWidth) { + contextMenuStyle = { + minWidth: targetWidth, + }; + } + } + + // The menu should only return if items were provided, if no items were provided then it should not appear. + if (items && items.length > 0) { + let totalItemCount = 0; + for (const item of items) { + if (item.itemType !== ContextualMenuItemType.Divider && item.itemType !== ContextualMenuItemType.Header) { + const itemCount = item.customOnRenderListLength ? item.customOnRenderListLength : 1; + totalItemCount += itemCount; + } + } + + const calloutStyles = this._classNames.subComponentStyles + ? (this._classNames.subComponentStyles.callout as IStyleFunctionOrObject< + ICalloutContentStyleProps, + ICalloutContentStyles + >) + : undefined; + + return ( + + ); + } else { + return null; + } + } + + /** + * Return whether the contextual menu is hidden. + * Undefined value for hidden is equivalent to hidden being false. + * @param props - Props for the component + */ + private _isHidden(props: IContextualMenuProps) { + return !!props.hidden; + } + + private _onMenuOpened() { + this._events.on(this._targetWindow, 'resize', this.dismiss); + this._shouldUpdateFocusOnMouseEvent = !this.props.delayUpdateFocusOnHover; + this._gotMouseMove = false; + this.props.onMenuOpened && this.props.onMenuOpened(this.props); + } + + private _onMenuClosed() { + this._events.off(this._targetWindow, 'resize', this.dismiss); + + // This is kept for backwards compatability with hidden for right now. + // This preserves the way that this behaved in the past + // TODO find a better way to handle this by using the same conventions that + // Popup uses to determine if focus is contained when dismissal occurs + this._tryFocusPreviousActiveElement({ + containsFocus: this._focusingPreviousElement, + originalElement: this._previousActiveElement, + }); + this._focusingPreviousElement = false; + + if (this.props.onMenuDismissed) { + this.props.onMenuDismissed(this.props); + } + + this._shouldUpdateFocusOnMouseEvent = !this.props.delayUpdateFocusOnHover; + + // We need to dismiss any submenu related state properties, + // so that when the menu is shown again, the submenu is collapsed + this.setState({ + expandedByMouseClick: undefined, + dismissedMenuItemKey: undefined, + expandedMenuItemKey: undefined, + submenuTarget: undefined, + }); + } + + private _tryFocusPreviousActiveElement = (options: { + containsFocus: boolean; + originalElement: HTMLElement | Window | undefined; + }) => { + if (options && options.containsFocus && this._previousActiveElement) { + // Make sure that the focus method actually exists + // In some cases the object might exist but not be a real element. + // This is primarily for IE 11 and should be removed once IE 11 is no longer in use. + if (this._previousActiveElement.focus) { + this._previousActiveElement.focus(); + } + } + }; + + /** + * Gets the focusZoneDirection by using the arrowDirection if specified, + * the direction specificed in the focusZoneProps, or defaults to FocusZoneDirection.vertical + */ + private _getFocusZoneDirection() { + const { focusZoneProps } = this.props; + return focusZoneProps && focusZoneProps.direction !== undefined + ? focusZoneProps.direction + : FocusZoneDirection.vertical; + } + + private _onRenderSubMenu( + subMenuProps: IContextualMenuProps, + defaultRender?: IRenderFunction, + ): JSX.Element { + throw Error( + 'ContextualMenuBase: onRenderSubMenu callback is null or undefined. ' + + 'Please ensure to set `onRenderSubMenu` property either manually or with `styled` helper.', + ); + } + + private _onRenderMenuList = ( + menuListProps: IContextualMenuListProps, + defaultRender?: IRenderFunction, + ): JSX.Element => { + let indexCorrection = 0; + const { items, totalItemCount, hasCheckmarks, hasIcons, role } = menuListProps; + return ( +
    + {items.map((item, index) => { + const menuItem = this._renderMenuItem(item, index, indexCorrection, totalItemCount, hasCheckmarks, hasIcons); + if (item.itemType !== ContextualMenuItemType.Divider && item.itemType !== ContextualMenuItemType.Header) { + const indexIncrease = item.customOnRenderListLength ? item.customOnRenderListLength : 1; + indexCorrection += indexIncrease; + } + return menuItem; + })} +
+ ); + }; + + /** + * !!!IMPORTANT!!! Avoid mutating `item: IContextualMenuItem` argument. It will + * cause the menu items to always re-render because the component update is based on shallow comparison. + */ + private _renderMenuItem = ( + item: IContextualMenuItem, + index: number, + focusableElementIndex: number, + totalItemCount: number, + hasCheckmarks: boolean, + hasIcons: boolean, + ): JSX.Element => { + const renderedItems: React.ReactNode[] = []; + const iconProps = item.iconProps || { iconName: 'None' }; + const { + getItemClassNames, // eslint-disable-line deprecation/deprecation + itemProps, + } = item; + const styles = itemProps ? itemProps.styles : undefined; + + // We only send a dividerClassName when the item to be rendered is a divider. + // For all other cases, the default divider style is used. + const dividerClassName = item.itemType === ContextualMenuItemType.Divider ? item.className : undefined; + const subMenuIconClassName = item.submenuIconProps ? item.submenuIconProps.className : ''; + + // eslint-disable-next-line deprecation/deprecation + let itemClassNames: IMenuItemClassNames; + + // IContextualMenuItem#getItemClassNames for backwards compatibility + // otherwise uses mergeStyles for class names. + if (getItemClassNames) { + itemClassNames = getItemClassNames( + this.props.theme!, + isItemDisabled(item), + this.state.expandedMenuItemKey === item.key, + !!getIsChecked(item), + !!item.href, + iconProps.iconName !== 'None', + item.className, + dividerClassName, + iconProps.className, + subMenuIconClassName, + item.primaryDisabled, + ); + } else { + const itemStyleProps: IContextualMenuItemStyleProps = { + theme: this.props.theme!, + disabled: isItemDisabled(item), + expanded: this.state.expandedMenuItemKey === item.key, + checked: !!getIsChecked(item), + isAnchorLink: !!item.href, + knownIcon: iconProps.iconName !== 'None', + itemClassName: item.className, + dividerClassName, + iconClassName: iconProps.className, + subMenuClassName: subMenuIconClassName, + primaryDisabled: item.primaryDisabled, + }; + + // We need to generate default styles then override if styles are provided + // since the ContextualMenu currently handles item classNames. + itemClassNames = getContextualMenuItemClassNames( + _getMenuItemStylesFunction(this._classNames.subComponentStyles?.menuItem, styles), + itemStyleProps, + ); + } + + // eslint-disable-next-line deprecation/deprecation + if (item.text === '-' || item.name === '-') { + item.itemType = ContextualMenuItemType.Divider; + } + switch (item.itemType) { + case ContextualMenuItemType.Divider: + renderedItems.push(this._renderSeparator(index, itemClassNames)); + break; + case ContextualMenuItemType.Header: + renderedItems.push(this._renderSeparator(index, itemClassNames)); + const headerItem = this._renderHeaderMenuItem(item, itemClassNames, index, hasCheckmarks, hasIcons); + renderedItems.push(this._renderListItem(headerItem, item.key || index, itemClassNames, item.title)); + break; + case ContextualMenuItemType.Section: + renderedItems.push(this._renderSectionItem(item, itemClassNames, index, hasCheckmarks, hasIcons)); + break; + default: + const menuItem = this._renderNormalItem( + item, + itemClassNames, + index, + focusableElementIndex, + totalItemCount, + hasCheckmarks, + hasIcons, + ); + renderedItems.push(this._renderListItem(menuItem, item.key || index, itemClassNames, item.title)); + break; + } + + // Since multiple nodes *could* be rendered, wrap them all in a fragment with this item's key. + // This ensures the reconciler handles multi-item output per-node correctly and does not re-mount content. + return {renderedItems}; + }; + + private _defaultMenuItemRenderer = (item: IContextualMenuItemRenderProps): React.ReactNode => { + const { index, focusableElementIndex, totalItemCount, hasCheckmarks, hasIcons } = item; + return this._renderMenuItem(item, index, focusableElementIndex, totalItemCount, hasCheckmarks, hasIcons); + }; + + private _renderSectionItem( + sectionItem: IContextualMenuItem, + // eslint-disable-next-line deprecation/deprecation + menuClassNames: IMenuItemClassNames, + index: number, + hasCheckmarks: boolean, + hasIcons: boolean, + ) { + const sectionProps = sectionItem.sectionProps; + if (!sectionProps) { + return; + } + + let headerItem; + let groupProps; + if (sectionProps.title) { + // Since title is a user-facing string, it needs to be stripped of whitespace in order to build a valid element ID + const id = this._id + sectionProps.title.replace(/\s/g, ''); + const headerContextualMenuItem: IContextualMenuItem = { + key: `section-${sectionProps.title}-title`, + itemType: ContextualMenuItemType.Header, + text: sectionProps.title, + id: id, + }; + groupProps = { + role: 'group', + 'aria-labelledby': id, + }; + headerItem = this._renderHeaderMenuItem(headerContextualMenuItem, menuClassNames, index, hasCheckmarks, hasIcons); + } + + if (sectionProps.items && sectionProps.items.length > 0) { + return ( +
  • +
    +
      + {sectionProps.topDivider && this._renderSeparator(index, menuClassNames, true, true)} + {headerItem && + this._renderListItem(headerItem, sectionItem.key || index, menuClassNames, sectionItem.title)} + {sectionProps.items.map((contextualMenuItem, itemsIndex) => + this._renderMenuItem( + contextualMenuItem, + itemsIndex, + itemsIndex, + sectionProps.items.length, + hasCheckmarks, + hasIcons, + ), + )} + {sectionProps.bottomDivider && this._renderSeparator(index, menuClassNames, false, true)} +
    +
    +
  • + ); + } + } + + private _renderListItem( + content: React.ReactNode, + key: string | number, + classNames: IMenuItemClassNames, // eslint-disable-line deprecation/deprecation + title?: string, + ) { + return ( +
  • + {content} +
  • + ); + } + + private _renderSeparator( + index: number, + classNames: IMenuItemClassNames, // eslint-disable-line deprecation/deprecation + top?: boolean, + fromSection?: boolean, + ): React.ReactNode { + if (fromSection || index > 0) { + return ( + + + + + + + + + + + +`; diff --git a/packages/react-next/src/components/ContextualMenu/__snapshots__/ContextualMenuItem.test.tsx.snap b/packages/react-next/src/components/ContextualMenu/__snapshots__/ContextualMenuItem.test.tsx.snap new file mode 100644 index 0000000000000..fccc4fec3e110 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/__snapshots__/ContextualMenuItem.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ContextMenuItemChildren when a checkmark icon renders the component with the checkmark 1`] = `ShallowWrapper {}`; + +exports[`ContextMenuItemChildren when hide checkmark icon for toggle command renders the component with the checkmark 1`] = `ShallowWrapper {}`; + +exports[`ContextMenuItemChildren when it has a sub menu renders the menu icon 1`] = `ShallowWrapper {}`; + +exports[`ContextMenuItemChildren when it has icons when it doesnt have iconProps renders the icon with iconName 1`] = `ShallowWrapper {}`; + +exports[`ContextMenuItemChildren when it has icons when it has iconProps renders the icon 1`] = `ShallowWrapper {}`; diff --git a/packages/react-next/src/components/ContextualMenu/docs/ContextualMenuBestPractices.md b/packages/react-next/src/components/ContextualMenu/docs/ContextualMenuBestPractices.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/react-next/src/components/ContextualMenu/docs/ContextualMenuDonts.md b/packages/react-next/src/components/ContextualMenu/docs/ContextualMenuDonts.md new file mode 100644 index 0000000000000..5c641e2dd5d6f --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/docs/ContextualMenuDonts.md @@ -0,0 +1,4 @@ +- Use them to display content. +- Show commands as one large group. +- Mix checks and icons. +- Create submenus of submenus. diff --git a/packages/react-next/src/components/ContextualMenu/docs/ContextualMenuDos.md b/packages/react-next/src/components/ContextualMenu/docs/ContextualMenuDos.md new file mode 100644 index 0000000000000..f629a0f175b3e --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/docs/ContextualMenuDos.md @@ -0,0 +1,4 @@ +- Use to display commands. +- Divide groups of commands with rules. +- Use selection checks without icons. +- Provide submenus for sets of related commands that aren’t as critical as others. diff --git a/packages/react-next/src/components/ContextualMenu/docs/ContextualMenuOverview.md b/packages/react-next/src/components/ContextualMenu/docs/ContextualMenuOverview.md new file mode 100644 index 0000000000000..5fe6dc43f8b9a --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/docs/ContextualMenuOverview.md @@ -0,0 +1,5 @@ +ContextualMenus are lists of commands that are based on the context of selection, mouse hover or keyboard focus. They are one of the most effective and highly used command surfaces, and can be used in a variety of places. + +There are variants that originate from a command bar, or from cursor or focus. Those that come from CommandBars use a beak that is horizontally centered on the button. Ones that come from right click and menu button do not have a beak, but appear to the right and below the cursor. ContextualMenus can have submenus from commands, show selection checks, and icons. + +Organize commands in groups divided by rules. This helps users remember command locations, or find less used commands based on proximity to others. One should also group sets of mutually exclusive or multiple selectable options. Use icons sparingly, for high value commands, and don’t mix icons with selection checks, as it makes parsing commands difficult. Avoid submenus of submenus as they can be difficult to invoke or remember. diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Basic.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Basic.Example.tsx new file mode 100644 index 0000000000000..22bfa6b7da497 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Basic.Example.tsx @@ -0,0 +1,85 @@ +import * as React from 'react'; +import { ContextualMenu, ContextualMenuItemType, IContextualMenuItem } from '@fluentui/react-next/lib/ContextualMenu'; +import { useConstCallback } from '@uifabric/react-hooks'; + +export const ContextualMenuBasicExample: React.FunctionComponent = () => { + const linkRef = React.useRef(null); + const [showContextualMenu, setShowContextualMenu] = React.useState(false); + const onShowContextualMenu = useConstCallback(() => setShowContextualMenu(true)); + const onHideContextualMenu = useConstCallback(() => setShowContextualMenu(false)); + + return ( +
    + This example directly uses ContextualMenu to show how it can be attached to arbitrary elements. The remaining + examples use ContextualMenu through Fluent UI Button components. +

    + + + Click for ContextualMenu + + +

    +
    + ); +}; + +const menuItems: IContextualMenuItem[] = [ + { + key: 'newItem', + text: 'New', + onClick: () => console.log('New clicked'), + }, + { + key: 'divider_1', + itemType: ContextualMenuItemType.Divider, + }, + { + key: 'rename', + text: 'Rename', + onClick: () => console.log('Rename clicked'), + }, + { + key: 'edit', + text: 'Edit', + onClick: () => console.log('Edit clicked'), + }, + { + key: 'properties', + text: 'Properties', + onClick: () => console.log('Properties clicked'), + }, + { + key: 'linkNoTarget', + text: 'Link same window', + href: 'http://bing.com', + }, + { + key: 'linkWithTarget', + text: 'Link new window', + href: 'http://bing.com', + target: '_blank', + }, + { + key: 'linkWithOnClick', + name: 'Link click', + href: 'http://bing.com', + onClick: (ev: React.MouseEvent) => { + alert('Link clicked'); + ev.preventDefault(); + }, + target: '_blank', + }, + { + key: 'disabled', + text: 'Disabled item', + disabled: true, + onClick: () => console.error('Disabled item should not be clickable.'), + }, +]; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Checkmarks.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Checkmarks.Example.tsx new file mode 100644 index 0000000000000..67443d7194df9 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Checkmarks.Example.tsx @@ -0,0 +1,216 @@ +import * as React from 'react'; +import { + ContextualMenuItemType, + DirectionalHint, + IContextualMenuItem, + IContextualMenuProps, +} from '@fluentui/react-next/lib/ContextualMenu'; +import { DefaultButton } from '@fluentui/react-next/lib/compat/Button'; + +const keys: string[] = [ + 'newItem', + 'share', + 'mobile', + 'enablePrint', + 'enableMusic', + 'newSub', + 'emailMessage', + 'calendarEvent', + 'disabledNewSub', + 'disabledEmailMessage', + 'disabledCalendarEvent', + 'splitButtonSubMenuLeftDirection', + 'emailMessageLeft', + 'calendarEventLeft', + 'disabledPrimarySplit', +]; + +export const ContextualMenuCheckmarksExample: React.FunctionComponent = () => { + const [selection, setSelection] = React.useState<{ [key: string]: boolean }>({}); + + const onToggleSelect = React.useCallback( + (ev?: React.MouseEvent, item?: IContextualMenuItem): void => { + ev && ev.preventDefault(); + + if (item) { + setSelection({ ...selection, [item.key]: selection[item.key] === undefined ? true : !selection[item.key] }); + } + }, + [selection], + ); + + const menuItems: IContextualMenuItem[] = React.useMemo( + () => [ + { + key: keys[0], + text: 'New', + canCheck: true, + isChecked: selection[keys[0]], + onClick: onToggleSelect, + }, + { + key: keys[1], + text: 'Share', + canCheck: true, + isChecked: selection[keys[1]], + onClick: onToggleSelect, + }, + { + key: keys[2], + text: 'Mobile', + canCheck: true, + isChecked: selection[keys[2]], + onClick: onToggleSelect, + }, + { + key: 'divider_1', + itemType: ContextualMenuItemType.Divider, + }, + + { + key: keys[3], + text: 'Print', + canCheck: true, + isChecked: selection[keys[3]], + onClick: onToggleSelect, + }, + { + key: keys[4], + text: 'Music', + canCheck: true, + isChecked: selection[keys[4]], + onClick: onToggleSelect, + }, + { + key: keys[5], + iconProps: { + iconName: 'MusicInCollectionFill', + }, + subMenuProps: { + items: [ + { + key: keys[6], + text: 'Email message', + canCheck: true, + isChecked: selection[keys[6]], + onClick: onToggleSelect, + }, + { + key: keys[7], + text: 'Calendar event', + canCheck: true, + isChecked: selection[keys[7]], + onClick: onToggleSelect, + }, + ], + }, + text: 'Split Button', + canCheck: true, + isChecked: selection[keys[5]], + split: true, + onClick: onToggleSelect, + }, + { + key: keys[8], + iconProps: { + iconName: 'MusicInCollectionFill', + }, + subMenuProps: { + items: [ + { + key: keys[9], + text: 'Email message', + canCheck: true, + isChecked: selection[keys[9]], + onClick: onToggleSelect, + }, + { + key: keys[10], + text: 'Calendar event', + canCheck: true, + isChecked: selection[keys[10]], + onClick: onToggleSelect, + }, + ], + }, + text: 'Split Button', + canCheck: true, + isChecked: selection[keys[8]], + split: true, + onClick: onToggleSelect, + disabled: true, + }, + { + key: keys[11], + iconProps: { + iconName: 'MusicInCollectionFill', + }, + subMenuProps: { + directionalHint: DirectionalHint.leftCenter, + items: [ + { + key: keys[12], + text: 'Email message', + canCheck: true, + isChecked: selection[keys[12]], + onClick: onToggleSelect, + }, + { + key: keys[13], + text: 'Calendar event', + canCheck: true, + isChecked: selection[keys[13]], + onClick: onToggleSelect, + }, + ], + }, + text: 'Split Button Left Menu', + canCheck: true, + isChecked: selection[keys[11]], + split: true, + onClick: onToggleSelect, + }, + { + key: keys[12], + iconProps: { + iconName: 'MusicInCollectionFill', + }, + subMenuProps: { + items: [ + { + key: keys[12], + name: 'Email message', + canCheck: true, + isChecked: selection[keys[12]], + onClick: onToggleSelect, + }, + ], + }, + name: 'Split Button Disabled Primary', + split: true, + primaryDisabled: true, + }, + { + key: keys[13], + iconProps: { + iconName: selection![keys[13]] ? 'SingleBookmarkSolid' : 'SingleBookmark', + }, + name: selection![keys[13]] ? 'Toogled command no checkmark' : 'Toogle command no checkmark', + canCheck: false, + isChecked: selection![keys[13]], + onClick: onToggleSelect, + }, + ], + [selection, onToggleSelect], + ); + + const menuProps: IContextualMenuProps = React.useMemo( + () => ({ + shouldFocusOnMount: true, + items: menuItems, + }), + [menuItems], + ); + + return ; +}; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.CustomMenuItem.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.CustomMenuItem.Example.tsx new file mode 100644 index 0000000000000..beee235416820 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.CustomMenuItem.Example.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; +import { + ContextualMenuItemType, + IContextualMenuProps, + IContextualMenuItem, + IContextualMenuItemProps, +} from '@fluentui/react-next/lib/ContextualMenu'; +import { DefaultButton } from '@fluentui/react-next/lib/compat/Button'; +import { useConst } from '@uifabric/react-hooks'; + +export const ContextualMenuWithCustomMenuItemExample: React.FunctionComponent = () => { + const menuProps: IContextualMenuProps = useConst(() => ({ + items: menuItems, + shouldFocusOnMount: true, + contextualMenuItemAs: (props: IContextualMenuItemProps) =>
    Custom rendered {props.item.text}
    , + })); + + return ; +}; + +const menuItems: IContextualMenuItem[] = [ + { + key: 'newItem', + text: 'New', + }, + { + key: 'divider_1', + itemType: ContextualMenuItemType.Divider, + }, + { + key: 'rename', + text: 'Rename', + }, + { + key: 'edit', + text: 'Edit', + }, + { + key: 'properties', + text: 'Properties', + }, + { + key: 'disabled', + text: 'Disabled item', + disabled: true, + }, +]; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.CustomMenuList.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.CustomMenuList.Example.tsx new file mode 100644 index 0000000000000..ed5b4c90192c9 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.CustomMenuList.Example.tsx @@ -0,0 +1,125 @@ +import * as React from 'react'; +import { useConstCallback, useConst } from '@uifabric/react-hooks'; +import { DefaultButton } from '@fluentui/react-next/lib/compat/Button'; +import { ISearchBoxStyles, SearchBox } from '@fluentui/react-next/lib/SearchBox'; +import { Icon } from '@fluentui/react-next/lib/Icon'; +import { IContextualMenuListProps, IContextualMenuItem } from '@fluentui/react-next/lib/ContextualMenu'; +import { IRenderFunction } from '@fluentui/react-next/lib/Utilities'; + +export const ContextualMenuWithCustomMenuListExample: React.FunctionComponent = () => { + const [items, setItems] = React.useState(menuItems); + + const onAbort = useConstCallback(() => { + setItems(menuItems); + }); + + const onChange = useConstCallback((ev: React.ChangeEvent, newValue: string) => { + const filteredItems = menuItems.filter( + item => item.text && item.text.toLowerCase().indexOf(newValue.toLowerCase()) !== -1, + ); + + if (!filteredItems || !filteredItems.length) { + filteredItems.push({ + key: 'no_results', + onRender: (item, dismissMenu) => ( +
    + + No actions found +
    + ), + }); + } + + setItems(filteredItems); + }); + + const renderMenuList = useConstCallback( + (menuListProps: IContextualMenuListProps, defaultRender: IRenderFunction) => { + return ( +
    +
    + +
    + {defaultRender(menuListProps)} +
    + ); + }, + ); + + const menuProps = useConst(() => ({ + onRenderMenuList: renderMenuList, + title: 'Actions', + shouldFocusOnMount: true, + items, + })); + + return ; +}; + +const wrapperStyle: React.CSSProperties = { borderBottom: '1px solid #ccc' }; +const filteredItemsStyle: React.CSSProperties = { + width: '100%', + height: '100px', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', +}; +const searchBoxStyles: ISearchBoxStyles = { + root: { margin: '8px' }, +}; + +const menuItems: IContextualMenuItem[] = [ + { + key: 'newItem', + text: 'New', + onClick: () => console.log('New clicked'), + }, + { + key: 'rename', + text: 'Rename', + onClick: () => console.log('Rename clicked'), + }, + { + key: 'edit', + text: 'Edit', + onClick: () => console.log('Edit clicked'), + }, + { + key: 'properties', + text: 'Properties', + onClick: () => console.log('Properties clicked'), + }, + { + key: 'linkNoTarget', + text: 'Link same window', + href: 'http://bing.com', + }, + { + key: 'linkWithTarget', + text: 'Link new window', + href: 'http://bing.com', + target: '_blank', + }, + { + key: 'linkWithOnClick', + name: 'Link click', + href: 'http://bing.com', + onClick: (ev: React.MouseEvent) => { + alert('Link clicked'); + ev.preventDefault(); + }, + target: '_blank', + }, + { + key: 'disabled', + text: 'Disabled item', + disabled: true, + onClick: () => console.error('Disabled item should not be clickable.'), + }, +]; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Customization.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Customization.Example.tsx new file mode 100644 index 0000000000000..3dc3bfce0fa61 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Customization.Example.tsx @@ -0,0 +1,225 @@ +import * as React from 'react'; +import { + ContextualMenuItemType, + DirectionalHint, + IContextualMenuProps, + IContextualMenuItem, +} from '@fluentui/react-next/lib/ContextualMenu'; +import { DefaultButton, IconButton } from '@fluentui/react-next/lib/compat/Button'; +import { FocusZoneDirection } from '@fluentui/react-next/lib/FocusZone'; +import './ContextualMenuExample.scss'; + +export const ContextualMenuCustomizationExample: React.FunctionComponent = () => { + return ; +}; + +function renderCharmMenuItem(item: IContextualMenuItem, dismissMenu: () => void): JSX.Element { + return ( + + ); +} + +function renderCategoriesList(item: IContextualMenuItem): JSX.Element { + return ( +
      +
    • + {item.categoryList.map((category: ICategoryList) => ( + +
      + + {category.name} +
      +
      + ))} +
    • +
    + ); +} + +interface ICategoryList { + name: string; + color: string; +} + +const menuItems: IContextualMenuItem[] = [ + { + key: 'newItem', + text: 'New', + }, + { + key: 'upload', + text: 'Upload', + }, + { + key: 'divider_1', + itemType: ContextualMenuItemType.Divider, + }, + { + key: 'charm', + text: 'Charm', + className: 'Charm-List', + subMenuProps: { + focusZoneProps: { direction: FocusZoneDirection.bidirectional }, + items: [ + { + key: 'none', + text: 'None', + }, + { + key: 'bulb', + text: 'Lightbulb', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'run', + text: 'Running', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'plane', + text: 'Airplane', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'page', + text: 'Page', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'cake', + text: 'Cake', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'soccer', + text: 'Soccer', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'home', + text: 'Home', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'emoji', + text: 'Emoji2', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'work', + text: 'Work', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'coffee', + text: 'Coffee', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'people', + text: 'People', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'stopwatch', + text: 'Stopwatch', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'music', + text: 'MusicInCollectionFill', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'lock', + text: 'Lock', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + ], + }, + }, + { + key: 'categories', + text: 'Categorize', + subMenuProps: { + items: [ + { + key: 'categories', + text: 'categories', + categoryList: [ + { + name: 'Personal', + color: 'yellow', + }, + { + name: 'Work', + color: 'green', + }, + { + name: 'Birthday', + color: 'blue', + }, + { + name: 'Spam', + color: 'grey', + }, + { + name: 'Urgent', + color: 'red', + }, + { + name: 'Hobbies', + color: 'black', + }, + ], + onRender: renderCategoriesList, + }, + { + key: 'divider_1', + itemType: ContextualMenuItemType.Divider, + }, + { + key: 'clear', + text: 'Clear categories', + }, + { + key: 'manage', + text: 'Manage categories', + }, + ], + }, + }, +]; + +const menuProps: IContextualMenuProps = { + shouldFocusOnMount: true, + directionalHint: DirectionalHint.bottomLeftEdge, + className: 'ms-ContextualMenu-customizationExample', + items: menuItems, +}; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.CustomizationWithNoWrap.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.CustomizationWithNoWrap.Example.tsx new file mode 100644 index 0000000000000..2ae022febc105 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.CustomizationWithNoWrap.Example.tsx @@ -0,0 +1,238 @@ +import * as React from 'react'; +import { + ContextualMenuItemType, + DirectionalHint, + IContextualMenuProps, + IContextualMenuItem, +} from '@fluentui/react-next/lib/ContextualMenu'; +import { DefaultButton, IconButton } from '@fluentui/react-next/lib/compat/Button'; +import { FocusZoneDirection } from '@fluentui/react-next/lib/FocusZone'; +import './ContextualMenuExample.scss'; + +export const ContextualMenuCustomizationWithNoWrapExample: React.FunctionComponent = () => { + return ; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function renderCharmMenuItem(item: any, dismissMenu: () => void): JSX.Element { + return ( + + ); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function renderCategoriesList(item: any): JSX.Element { + return ( +
      +
    • + {item.categoryList.map((category: ICategoryList) => ( + +
      + + {category.name} +
      +
      + ))} +
    • +
    + ); +} + +interface ICategoryList { + name: string; + color: string; +} + +const menuItems: IContextualMenuItem[] = [ + { + key: 'newItem', + text: 'New', + }, + { + key: 'upload', + text: 'Upload', + }, + { + key: 'divider_1', + itemType: ContextualMenuItemType.Divider, + }, + { + key: 'charm', + text: 'Charm', + className: 'Charm-List', + subMenuProps: { + focusZoneProps: { + direction: FocusZoneDirection.bidirectional, + checkForNoWrap: true, + }, + items: [ + { + key: 'bulb', + text: 'Lightbulb', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'run', + text: 'Running', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'plane', + text: 'Airplane', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'page', + text: 'Page', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'cake', + text: 'Cake', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'soccer', + text: 'Soccer', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'home', + text: 'Home', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'emoji', + text: 'Emoji2', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'work', + text: 'Work', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'coffee', + text: 'Coffee', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'people', + text: 'People', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'stopwatch', + text: 'Stopwatch', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'music', + text: 'MusicInCollectionFill', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'lock', + text: 'Lock', + onRender: renderCharmMenuItem, + className: 'ms-ContextualMenu-customizationExample-item', + }, + { + key: 'item3', + text: 'Item 3', + 'data-no-horizontal-wrap': true, + }, + { + key: 'item4', + text: 'Item 4', + 'data-no-horizontal-wrap': true, + }, + ], + }, + }, + { + key: 'categories', + text: 'Categorize', + subMenuProps: { + items: [ + { + key: 'categories', + text: 'categories', + categoryList: [ + { + name: 'Personal', + color: 'yellow', + }, + { + name: 'Work', + color: 'green', + }, + { + name: 'Birthday', + color: 'blue', + }, + { + name: 'Spam', + color: 'grey', + }, + { + name: 'Urgent', + color: 'red', + }, + { + name: 'Hobbies', + color: 'black', + }, + ], + onRender: renderCategoriesList, + }, + { + key: 'divider_1', + itemType: ContextualMenuItemType.Divider, + }, + { + key: 'clear', + text: 'Additional Item', + }, + { + key: 'manage', + text: 'Additional Item', + }, + ], + }, + }, +]; + +const menuProps: IContextualMenuProps = { + shouldFocusOnMount: true, + directionalHint: DirectionalHint.bottomLeftEdge, + className: 'ms-ContextualMenu-customizationExample', + items: menuItems, +}; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Default.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Default.Example.tsx new file mode 100644 index 0000000000000..6a023d607350a --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Default.Example.tsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import { + ContextualMenuItemType, + IContextualMenuProps, + IContextualMenuItem, +} from '@fluentui/react-next/lib/ContextualMenu'; +import { DefaultButton } from '@fluentui/react-next/lib/compat/Button'; + +export const ContextualMenuDefaultExample: React.FunctionComponent = () => { + return ; +}; + +const menuItems: IContextualMenuItem[] = [ + { + key: 'newItem', + text: 'New', + onClick: () => console.log('New clicked'), + }, + { + key: 'divider_1', + itemType: ContextualMenuItemType.Divider, + }, + { + key: 'rename', + text: 'Rename', + onClick: () => console.log('Rename clicked'), + }, + { + key: 'edit', + text: 'Edit', + onClick: () => console.log('Edit clicked'), + }, + { + key: 'properties', + text: 'Properties', + onClick: () => console.log('Properties clicked'), + }, + { + key: 'linkNoTarget', + text: 'Link same window', + href: 'http://bing.com', + }, + { + key: 'linkWithTarget', + text: 'Link new window', + href: 'http://bing.com', + target: '_blank', + }, + { + key: 'linkWithOnClick', + name: 'Link click', + href: 'http://bing.com', + onClick: (ev: React.MouseEvent) => { + alert('Link clicked'); + ev.preventDefault(); + }, + target: '_blank', + }, + { + key: 'disabled', + text: 'Disabled item', + disabled: true, + onClick: () => console.error('Disabled item should not be clickable.'), + }, +]; + +const menuProps: IContextualMenuProps = { + shouldFocusOnMount: true, + items: menuItems, +}; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Directional.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Directional.Example.tsx new file mode 100644 index 0000000000000..58c25f0b03d91 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Directional.Example.tsx @@ -0,0 +1,131 @@ +import * as React from 'react'; +import { useConstCallback } from '@uifabric/react-hooks'; +import { DefaultButton } from '@fluentui/react-next/lib/compat/Button'; +import { Checkbox, ICheckboxStyles } from '@fluentui/react-next/lib/Checkbox'; +import { + ContextualMenuItemType, + DirectionalHint, + IContextualMenuProps, + IContextualMenuItem, +} from '@fluentui/react-next/lib/ContextualMenu'; +import { Dropdown, IDropdownOption } from '@fluentui/react-next/lib/Dropdown'; +import { getRTL } from '@fluentui/react-next/lib/Utilities'; +import './ContextualMenuExample.scss'; + +const DIRECTION_OPTIONS = [ + { key: DirectionalHint.topLeftEdge, text: 'Top Left Edge' }, + { key: DirectionalHint.topCenter, text: 'Top Center' }, + { key: DirectionalHint.topRightEdge, text: 'Top Right Edge' }, + { key: DirectionalHint.topAutoEdge, text: 'Top Auto Edge' }, + { key: DirectionalHint.bottomLeftEdge, text: 'Bottom Left Edge' }, + { key: DirectionalHint.bottomCenter, text: 'Bottom Center' }, + { key: DirectionalHint.bottomRightEdge, text: 'Bottom Right Edge' }, + { key: DirectionalHint.bottomAutoEdge, text: 'Bottom Auto Edge' }, + { key: DirectionalHint.leftTopEdge, text: 'Left Top Edge' }, + { key: DirectionalHint.leftCenter, text: 'Left Center' }, + { key: DirectionalHint.leftBottomEdge, text: 'Left Bottom Edge' }, + { key: DirectionalHint.rightTopEdge, text: 'Right Top Edge' }, + { key: DirectionalHint.rightCenter, text: 'Right Center' }, + { key: DirectionalHint.rightBottomEdge, text: 'Right Bottom Edge' }, +]; + +const checkboxStyles: Partial = { root: { margin: '10px 0' } }; + +export const ContextualMenuDirectionalExample: React.FunctionComponent = () => { + const [isBeakVisible, setIsBeakVisible] = React.useState(false); + const [useDirectionalHintForRTL, setUseDirectionalHintForRTL] = React.useState(false); + const [directionalHint, setDirectionalHint] = React.useState(DirectionalHint.bottomLeftEdge); + const [directionalHintForRTL, setDirectionalHintForRTL] = React.useState( + DirectionalHint.bottomLeftEdge, + ); + + const onShowBeakChange = useConstCallback((event: React.FormEvent, isVisible: boolean): void => { + setIsBeakVisible(isVisible); + }); + + const onUseRtlHintChange = useConstCallback((event: React.FormEvent, isVisible: boolean): void => { + setUseDirectionalHintForRTL(isVisible); + }); + + const onDirectionalChanged = useConstCallback( + (event: React.FormEvent, option: IDropdownOption): void => { + setDirectionalHint(option.key as DirectionalHint); + }, + ); + + const onDirectionalRtlChanged = useConstCallback( + (event: React.FormEvent, option: IDropdownOption): void => { + setDirectionalHintForRTL(option.key as DirectionalHint); + }, + ); + + const menuProps: IContextualMenuProps = React.useMemo( + () => ({ + isBeakVisible: isBeakVisible, + directionalHint: directionalHint, + directionalHintForRTL: useDirectionalHintForRTL ? directionalHintForRTL : undefined, + gapSpace: 0, + beakWidth: 20, + directionalHintFixed: false, + items: menuItems, + }), + [isBeakVisible, directionalHint, directionalHintForRTL, useDirectionalHintForRTL], + ); + + return ( +
    +
    + + + {getRTL() && ( + + )} + {getRTL() && ( + + )} +
    +
    + +
    +
    + ); +}; + +const menuItems: IContextualMenuItem[] = [ + { + key: 'newItem', + text: 'New', + }, + { + key: 'divider_1', + itemType: ContextualMenuItemType.Divider, + }, + { + key: 'rename', + text: 'Rename', + }, + { + key: 'edit', + text: 'Edit', + }, + { + key: 'properties', + text: 'Properties', + }, + { + key: 'disabled', + text: 'Disabled item', + disabled: true, + }, +]; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Header.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Header.Example.tsx new file mode 100644 index 0000000000000..ee6625ac05d46 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Header.Example.tsx @@ -0,0 +1,94 @@ +import * as React from 'react'; +import { DefaultButton } from '@fluentui/react-next/lib/compat/Button'; +import { + ContextualMenuItemType, + IContextualMenuProps, + IContextualMenuItem, +} from '@fluentui/react-next/lib/ContextualMenu'; + +export const ContextualMenuHeaderExample: React.FunctionComponent = () => { + return ; +}; + +const menuItems: IContextualMenuItem[] = [ + { + key: 'Actions', + itemType: ContextualMenuItemType.Header, + text: 'Actions', + itemProps: { + lang: 'en-us', + }, + }, + { + key: 'upload', + iconProps: { + iconName: 'Upload', + style: { + color: 'salmon', + }, + }, + text: 'Upload', + title: 'Upload a file', + }, + { + key: 'rename', + text: 'Rename', + }, + { + key: 'share', + iconProps: { + iconName: 'Share', + }, + subMenuProps: { + items: [ + { + key: 'sharetoemail', + text: 'Share to Email', + iconProps: { + iconName: 'Mail', + }, + }, + { + key: 'sharetofacebook', + text: 'Share to Facebook', + }, + { + key: 'sharetotwitter', + text: 'Share to Twitter', + iconProps: { + iconName: 'Share', + }, + }, + ], + }, + text: 'Sharing', + }, + { + key: 'navigation', + itemType: ContextualMenuItemType.Header, + text: 'Navigation', + }, + { + key: 'properties', + text: 'Properties', + }, + { + key: 'print', + iconProps: { + iconName: 'Print', + }, + text: 'Print', + }, + + { + key: 'Bing', + text: 'Go to Bing', + href: 'http://www.bing.com', + target: '_blank', + }, +]; + +const menuProps: IContextualMenuProps = { + shouldFocusOnMount: true, + items: menuItems, +}; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Icon.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Icon.Example.tsx new file mode 100644 index 0000000000000..e370c4d660a71 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Icon.Example.tsx @@ -0,0 +1,96 @@ +import * as React from 'react'; +import { useConst, useConstCallback } from '@uifabric/react-hooks'; +import { DefaultButton } from '@fluentui/react-next/lib/compat/Button'; +import { Callout } from '@fluentui/react-next/lib/Callout'; +import { + ContextualMenuItemType, + IContextualMenuProps, + IContextualMenuItem, + IContextualMenuItemProps, +} from '@fluentui/react-next/lib/ContextualMenu'; +import { Icon } from '@fluentui/react-next/lib/Icon'; +import * as stylesImport from './ContextualMenuExample.scss'; + +const styles = stylesImport; + +export const ContextualMenuIconExample: React.FunctionComponent = () => { + const [showCallout, setShowCallout] = React.useState(false); + + const onShowCallout = useConstCallback(() => setShowCallout(true)); + const onHideCallout = useConstCallback(() => setShowCallout(false)); + + const menuItems: IContextualMenuItem[] = useConst([ + { + key: 'openInWord', + text: 'Open in Word', + onRenderIcon: (props: IContextualMenuItemProps) => { + return ( + + + + + ); + }, + }, + { + key: 'newItem', + iconProps: { + iconName: 'Add', + }, + text: 'New', + }, + { + key: 'upload', + onClick: onShowCallout, + iconProps: { + iconName: 'Upload', + style: { + color: 'salmon', + }, + }, + text: 'Upload (Click for popup)', + title: 'Upload a file', + }, + { + key: 'divider_1', + itemType: ContextualMenuItemType.Divider, + }, + { + key: 'share', + iconProps: { + iconName: 'Share', + }, + text: 'Share', + }, + { + key: 'print', + iconProps: { + iconName: 'Print', + }, + text: 'Print', + }, + { + key: 'music', + iconProps: { + iconName: 'MusicInCollectionFill', + }, + text: 'Music', + }, + ]); + + const menuProps: IContextualMenuProps = useConst({ + shouldFocusOnMount: true, + items: menuItems, + }); + + return ( +
    + + {showCallout && ( + + + + )} +
    + ); +}; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Icon.SecondaryText.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Icon.SecondaryText.Example.tsx new file mode 100644 index 0000000000000..38c2b543d05a1 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Icon.SecondaryText.Example.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; +import { DefaultButton } from '@fluentui/react-next/lib/compat/Button'; +import { IContextualMenuProps, IContextualMenuItem } from '@fluentui/react-next/lib/ContextualMenu'; + +export const ContextualMenuIconSecondaryTextExample: React.FunctionComponent = () => { + return ; +}; + +const menuItems: IContextualMenuItem[] = [ + { + key: 'Later Today', + iconProps: { + iconName: 'Clock', + }, + text: 'Later Today', + secondaryText: '7:00 PM', + }, + { + key: 'Tomorrow', + iconProps: { + iconName: 'Coffeescript', + }, + text: 'Tomorrow', + secondaryText: 'Thu. 8:00 AM', + }, + { + key: 'This Weekend', + iconProps: { + iconName: 'Vacation', + }, + text: 'This Weekend', + secondaryText: 'Sat. 10:00 AM', + }, + { + key: 'Next Week', + iconProps: { + iconName: 'Suitcase', + }, + text: 'Next Week', + secondaryText: 'Mon. 8:00 AM', + }, +]; + +const menuProps: IContextualMenuProps = { + shouldFocusOnMount: true, + items: menuItems, +}; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Persisted.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Persisted.Example.tsx new file mode 100644 index 0000000000000..33fbe0a4bd9fe --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Persisted.Example.tsx @@ -0,0 +1,86 @@ +import * as React from 'react'; +import { + ContextualMenuItemType, + IContextualMenuProps, + IContextualMenuItem, +} from '@fluentui/react-next/lib/ContextualMenu'; +import { DefaultButton } from '@fluentui/react-next/lib/compat/Button'; + +export const ContextualMenuPersistedExample: React.FunctionComponent = () => { + return ; +}; + +const menuItems: IContextualMenuItem[] = [ + { + key: 'newItem', + subMenuProps: { + items: [ + { + key: 'emailMessage', + text: 'Email message', + title: 'Create an email', + }, + { + key: 'calendarEvent', + text: 'Calendar event', + title: 'Create a calendar event', + }, + ], + }, + href: 'https://bing.com', + text: 'New', + target: '_blank', + }, + { + key: 'divider_1', + itemType: ContextualMenuItemType.Divider, + }, + { + key: 'rename', + text: 'Rename', + onClick: () => console.log('Rename clicked'), + }, + { + key: 'edit', + text: 'Edit', + onClick: () => console.log('Edit clicked'), + }, + { + key: 'properties', + text: 'Properties', + onClick: () => console.log('Properties clicked'), + }, + { + key: 'linkNoTarget', + text: 'Link same window', + href: 'http://bing.com', + }, + { + key: 'linkWithTarget', + text: 'Link new window', + href: 'http://bing.com', + target: '_blank', + }, + { + key: 'linkWithOnClick', + name: 'Link click', + href: 'http://bing.com', + onClick: (ev: React.MouseEvent) => { + alert('Link clicked'); + ev.preventDefault(); + }, + target: '_blank', + }, + { + key: 'disabled', + text: 'Disabled item', + disabled: true, + onClick: () => console.error('Disabled item should not be clickable.'), + }, +]; + +const menuProps: IContextualMenuProps = { + shouldFocusOnMount: true, + shouldFocusOnContainer: true, + items: menuItems, +}; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.ScrollBar.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.ScrollBar.Example.tsx new file mode 100644 index 0000000000000..d65a5278531e6 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.ScrollBar.Example.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import { DirectionalHint, IContextualMenuProps, IContextualMenuItem } from '@fluentui/react-next/lib/ContextualMenu'; +import { DefaultButton } from '@fluentui/react-next/lib/compat/Button'; + +export const ContextualMenuWithScrollBarExample: React.FunctionComponent = () => { + return ; +}; + +const menuItems: IContextualMenuItem[] = [ + { + key: 'newItem', + text: 'New', + }, + { + key: 'item 2', + text: 'Item with a very long label text', + }, + { + key: 'edit', + text: 'Edit', + }, + { + key: 'properties', + text: 'Properties', + }, + { + key: 'disabled', + text: 'Disabled item', + disabled: true, + }, +]; + +const menuProps: IContextualMenuProps = { + shouldFocusOnMount: true, + directionalHint: DirectionalHint.bottomRightEdge, + directionalHintFixed: true, + items: menuItems, + calloutProps: { + calloutMaxHeight: 65, + }, +}; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Section.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Section.Example.tsx new file mode 100644 index 0000000000000..f263342fd4a31 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Section.Example.tsx @@ -0,0 +1,71 @@ +import * as React from 'react'; +import { + ContextualMenuItemType, + IContextualMenuProps, + IContextualMenuItem, +} from '@fluentui/react-next/lib/ContextualMenu'; +import { DefaultButton } from '@fluentui/react-next/lib/compat/Button'; + +export const ContextualMenuSectionExample: React.FunctionComponent = () => { + return ; +}; + +const menuItems: IContextualMenuItem[] = [ + { + key: 'section1', + itemType: ContextualMenuItemType.Section, + sectionProps: { + topDivider: true, + bottomDivider: true, + title: 'Actions', + items: [ + { + key: 'newItem', + text: 'New', + }, + { + key: 'deleteItem', + text: 'Delete', + }, + ], + }, + }, + { + key: 'section2', + itemType: ContextualMenuItemType.Section, + sectionProps: { + title: 'Social', + items: [ + { + key: 'share', + text: 'Share', + }, + { + key: 'print', + text: 'Print', + }, + { + key: 'music', + text: 'Music', + }, + ], + }, + }, + { + key: 'section3', + itemType: ContextualMenuItemType.Section, + sectionProps: { + title: 'Navigation', + items: [ + { + key: 'Bing', + text: 'Go to Bing', + href: 'http://www.bing.com', + target: '_blank', + }, + ], + }, + }, +]; + +const menuProps: IContextualMenuProps = { items: menuItems }; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Submenu.Example.tsx b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Submenu.Example.tsx new file mode 100644 index 0000000000000..05dab927f03f9 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenu.Submenu.Example.tsx @@ -0,0 +1,142 @@ +import * as React from 'react'; +import { useConstCallback } from '@uifabric/react-hooks'; +import { DefaultButton } from '@fluentui/react-next/lib/compat/Button'; +import { IContextualMenuProps, IContextualMenuItem } from '@fluentui/react-next/lib/ContextualMenu'; +import { TextField, ITextFieldStyles } from '@fluentui/react-next/lib/TextField'; + +const textFieldStyles: Partial = { + subComponentStyles: { + label: { root: { display: 'inline-block', marginRight: '10px' } }, + }, + fieldGroup: { display: 'inline-flex', maxWidth: '100px' }, + wrapper: { display: 'block', marginBottom: '10px' }, +}; + +export interface IContextualMenuSubmenuExampleState { + hoverDelay: number; +} + +export const ContextualMenuSubmenuExample: React.FunctionComponent = () => { + const [hoverDelay, setHoverDelay] = React.useState(250); + + const onHoverDelayChanged = useConstCallback( + (ev: React.FormEvent, newValue: string) => { + setHoverDelay(Number(newValue) || 0); + }, + ); + + const menuProps: IContextualMenuProps = React.useMemo( + () => ({ + shouldFocusOnMount: true, + subMenuHoverDelay: hoverDelay, + items: menuItems, + }), + [hoverDelay], + ); + + return ( +
    + + +
    + ); +}; + +const menuItems: IContextualMenuItem[] = [ + { + key: 'newItem', + subMenuProps: { + items: [ + { + key: 'emailMessage', + text: 'Email message', + title: 'Create an email', + }, + { + key: 'calendarEvent', + text: 'Calendar event', + title: 'Create a calendar event', + }, + ], + }, + href: 'https://bing.com', + text: 'New', + target: '_blank', + }, + { + key: 'share', + subMenuProps: { + items: [ + { + key: 'sharetotwitter', + text: 'Share to Twitter', + }, + { + key: 'sharetofacebook', + text: 'Share to Facebook', + }, + { + key: 'sharetoemail', + text: 'Share to Email', + subMenuProps: { + items: [ + { + key: 'sharetooutlook_1', + text: 'Share to Outlook', + title: 'Share to Outlook', + }, + { + key: 'sharetogmail_1', + text: 'Share to Gmail', + title: 'Share to Gmail', + }, + ], + }, + }, + ], + }, + text: 'Share', + }, + { + key: 'shareSplit', + split: true, + 'aria-roledescription': 'split button', + subMenuProps: { + items: [ + { + key: 'sharetotwittersplit', + text: 'Share to Twitter', + }, + { + key: 'sharetofacebooksplit', + text: 'Share to Facebook', + }, + { + key: 'sharetoemailsplit', + text: 'Share to Email', + subMenuProps: { + items: [ + { + key: 'sharetooutlooksplit_1', + text: 'Share to Outlook', + title: 'Share to Outlook', + }, + { + key: 'sharetogmailsplit_1', + text: 'Share to Gmail', + title: 'Share to Gmail', + }, + ], + }, + }, + ], + }, + text: 'Share w/ Split', + }, +]; diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenuExample.scss b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenuExample.scss new file mode 100644 index 0000000000000..be13c717ca5c0 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenuExample.scss @@ -0,0 +1,94 @@ +@import '../../../common/common'; + +:global { + .ms-ContextualMenuDirectionalExample { + position: relative; + } + + .ms-ContextualMenuDirectionalExample-configArea, + .ms-ContextualMenuDirectionalExample-buttonArea { + display: inline-block; + min-width: 300px; + } + + .ms-ContextualMenuDirectionalExample-buttonArea { + position: absolute; + vertical-align: top; + width: 300px; + top: 70px; + margin: 0 100px; + text-align: center; + } + + .ms-ContextualMenuDirectionalExample-buttonArea .ms-Button { + text-align: center; + width: 100%; + } + + .ms-ContextualMenu-customizationExample-item { + display: inline-block; + width: 40px; + height: 40px; + line-height: 40px; + text-align: center; + vertical-align: middle; + margin-bottom: 8px; + cursor: pointer; + + &:hover { + background-color: #eaeaea; + } + } + + .ms-ContextualMenu-customizationExample-categoriesList { + margin: 0px; + padding: 0; + list-style-type: none; + } + + .ms-ContextualMenu-customizationExample-categorySwatch { + width: 24px; + height: 24px; + vertical-align: top; + } + + .ms-ContextualMenu-example-clickableArea { + background: lightblue; + border: none; + font-size: 0; + height: 200px; + width: 100%; + } + + .ms-ContextualMenu-customizationExample { + text-align: center; + max-width: 180px; + .ms-ContextualMenu-item { + height: auto; + } + } + + .ms-ContextualMenu-customizationExample-button { + width: 40%; + margin: 2%; + } +} + +.iconContainer { + position: relative; + margin: 0 4px; + height: 32px; + width: 14px; +} + +.logoFillIcon, +.logoIcon { + position: absolute; + left: 0; + right: 0; + color: $ms-color-themeDarkAlt; +} + +.logoFillIcon { + color: #ffffff; +} diff --git a/packages/react-next/src/components/ContextualMenu/index.ts b/packages/react-next/src/components/ContextualMenu/index.ts new file mode 100644 index 0000000000000..1874e3d39d1c9 --- /dev/null +++ b/packages/react-next/src/components/ContextualMenu/index.ts @@ -0,0 +1,6 @@ +export * from './ContextualMenu'; +export * from './ContextualMenu.base'; +export * from './ContextualMenu.types'; +export * from './ContextualMenuItem'; +export * from './ContextualMenuItem.base'; +export * from './ContextualMenuItem.types'; diff --git a/packages/react-next/src/utilities/contextualMenu/contextualMenuUtility.test.ts b/packages/react-next/src/utilities/contextualMenu/contextualMenuUtility.test.ts new file mode 100644 index 0000000000000..373382b8b3728 --- /dev/null +++ b/packages/react-next/src/utilities/contextualMenu/contextualMenuUtility.test.ts @@ -0,0 +1,116 @@ +import { getIsChecked, hasSubmenu, getMenuItemAriaRole } from './contextualMenuUtility'; +import { IContextualMenuItem } from '../../index'; + +describe('getIsChecked', () => { + describe('when item can be checked', () => { + let menuItem: IContextualMenuItem; + + beforeEach(() => { + menuItem = { key: '123', canCheck: true }; + }); + + it('returns true when isChecked', () => { + menuItem.isChecked = true; + expect(getIsChecked(menuItem)).toBe(true); + }); + + it('returns true when checked', () => { + menuItem.checked = true; + expect(getIsChecked(menuItem)).toBe(true); + }); + }); + + it('when item cannot be checked', () => { + const menuItem: IContextualMenuItem = { + key: '123', + canCheck: false, + }; + expect(getIsChecked(menuItem)).toBe(null); + }); + + describe('when item isChecked', () => { + let menuItem: IContextualMenuItem; + + beforeEach(() => { + menuItem = { key: '123', isChecked: true }; + }); + + it('returns true', () => { + expect(getIsChecked(menuItem)).toBe(true); + }); + }); + + describe('when item checked flag is true', () => { + let menuItem: IContextualMenuItem; + + beforeEach(() => { + menuItem = { key: '123', checked: true }; + }); + + it('returns true', () => { + expect(getIsChecked(menuItem)).toBe(true); + }); + }); + + describe('when it is not checked', () => { + let menuItem: IContextualMenuItem; + + beforeEach(() => { + menuItem = { key: '123' }; + }); + + it('returns false', () => { + expect(getIsChecked(menuItem)).toBeFalsy(); + }); + }); +}); + +describe('getMenuItemAriaRole', () => { + it('menu item is checkbox', () => { + const menuItem: IContextualMenuItem = { key: '123', canCheck: true, checked: false }; + expect(getMenuItemAriaRole(menuItem)).toBe('menuitemcheckbox'); + }); + + it('menu item is not checkbox', () => { + const menuItem: IContextualMenuItem = { key: '123', canCheck: false }; + expect(getMenuItemAriaRole(menuItem)).toBe('menuitem'); + }); +}); + +describe('hasSubmenu', () => { + describe('when there is a submenu props', () => { + let menuItem: IContextualMenuItem; + + beforeEach(() => { + menuItem = { key: '123', subMenuProps: { items: [] } }; + }); + + it('returns true', () => { + expect(hasSubmenu(menuItem)).toBe(true); + }); + }); + + describe('when there are items', () => { + let menuItem: IContextualMenuItem; + + beforeEach(() => { + menuItem = { key: '123', items: [] }; + }); + + it('returns true', () => { + expect(hasSubmenu(menuItem)).toBe(true); + }); + }); + + describe('when there are no submenu items', () => { + let menuItem: IContextualMenuItem; + + beforeEach(() => { + menuItem = { key: '123' }; + }); + + it('returns false', () => { + expect(hasSubmenu(menuItem)).toBe(false); + }); + }); +}); diff --git a/packages/react-next/src/utilities/contextualMenu/contextualMenuUtility.ts b/packages/react-next/src/utilities/contextualMenu/contextualMenuUtility.ts new file mode 100644 index 0000000000000..6464665e55704 --- /dev/null +++ b/packages/react-next/src/utilities/contextualMenu/contextualMenuUtility.ts @@ -0,0 +1,40 @@ +import { IContextualMenuItem } from '../../index'; + +/** + * Determines the effective checked state of a menu item. + * + * @param item {IContextualMenuItem} to get the check state of. + * @returns {true} if the item is checked. + * @returns {false} if the item is unchecked. + * @returns {null} if the item is not checkable. + */ +export function getIsChecked(item: IContextualMenuItem): boolean | null { + if (item.canCheck) { + return !!(item.isChecked || item.checked); + } + + if (typeof item.isChecked === 'boolean') { + return item.isChecked; + } + + if (typeof item.checked === 'boolean') { + return item.checked; + } + + // Item is not checkable. + return null; +} + +export function hasSubmenu(item: IContextualMenuItem): boolean { + return !!(item.subMenuProps || item.items); +} + +export function isItemDisabled(item: IContextualMenuItem): boolean { + return !!(item.isDisabled || item.disabled); +} + +export function getMenuItemAriaRole(item: IContextualMenuItem): string { + const isChecked = getIsChecked(item); + const canCheck: boolean = isChecked !== null; + return canCheck ? 'menuitemcheckbox' : 'menuitem'; +} diff --git a/packages/react-next/src/utilities/contextualMenu/index.ts b/packages/react-next/src/utilities/contextualMenu/index.ts new file mode 100644 index 0000000000000..5ab11502d63a0 --- /dev/null +++ b/packages/react-next/src/utilities/contextualMenu/index.ts @@ -0,0 +1 @@ +export * from './contextualMenuUtility'; From c4b8a6b315f6ebda559c94255683e9cf9b1f0577 Mon Sep 17 00:00:00 2001 From: Michael Loughry Date: Thu, 6 Aug 2020 20:30:01 +0000 Subject: [PATCH 2/9] Fix test --- packages/react-next/src/components/Slider/Slider.test.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/react-next/src/components/Slider/Slider.test.tsx b/packages/react-next/src/components/Slider/Slider.test.tsx index 64eb662bd17b8..82e3cde1e4307 100644 --- a/packages/react-next/src/components/Slider/Slider.test.tsx +++ b/packages/react-next/src/components/Slider/Slider.test.tsx @@ -8,7 +8,6 @@ import { mount, ReactWrapper } from 'enzyme'; import { Slider } from './Slider'; import { ISlider } from './Slider.types'; import { ONKEYDOWN_TIMEOUT_DURATION } from './Slider.base'; -import { sharedIsConformant } from '../../common/sharedIsConformant'; import { resetIds, KeyCodes } from '@uifabric/utilities'; describe('Slider', () => { @@ -25,12 +24,6 @@ describe('Slider', () => { } }); - sharedIsConformant({ - componentPath: path.join(__dirname, 'Slider.tsx'), - Component: Slider, - displayName: 'Slider', - }); - it('renders correctly', () => { const component = create(); const tree = component.toJSON(); From dff1290d8265a607dc01d188c4dcae63c9a067b8 Mon Sep 17 00:00:00 2001 From: Michael Loughry Date: Thu, 6 Aug 2020 20:30:37 +0000 Subject: [PATCH 3/9] Change files --- ...020-08-06-20-30-37-react-next-copy-ContextualMenu.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 change/@fluentui-react-next-2020-08-06-20-30-37-react-next-copy-ContextualMenu.json diff --git a/change/@fluentui-react-next-2020-08-06-20-30-37-react-next-copy-ContextualMenu.json b/change/@fluentui-react-next-2020-08-06-20-30-37-react-next-copy-ContextualMenu.json new file mode 100644 index 0000000000000..08d5c98e42fb2 --- /dev/null +++ b/change/@fluentui-react-next-2020-08-06-20-30-37-react-next-copy-ContextualMenu.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "Copy ContextualMenu to react-next", + "packageName": "@fluentui/react-next", + "email": "miclo@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-08-06T20:30:37.314Z" +} From c465e85438f7154721f9894ee6d5d1dba8e87075 Mon Sep 17 00:00:00 2001 From: Michael Loughry Date: Thu, 6 Aug 2020 21:41:30 +0000 Subject: [PATCH 4/9] Fix build --- packages/react-next/src/components/Slider/Slider.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-next/src/components/Slider/Slider.test.tsx b/packages/react-next/src/components/Slider/Slider.test.tsx index 82e3cde1e4307..2400a08f40dac 100644 --- a/packages/react-next/src/components/Slider/Slider.test.tsx +++ b/packages/react-next/src/components/Slider/Slider.test.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { create } from '@uifabric/utilities/lib/test'; import * as ReactTestUtils from 'react-dom/test-utils'; -import * as path from 'path'; import { mount, ReactWrapper } from 'enzyme'; import { Slider } from './Slider'; From b8d8583624420a66a60bee34f50ee1341b250095 Mon Sep 17 00:00:00 2001 From: Michael Loughry Date: Thu, 6 Aug 2020 22:33:32 +0000 Subject: [PATCH 5/9] Fix lint issues --- .../ContextualMenuItemWrapper.types.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuItemWrapper.types.ts b/packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuItemWrapper.types.ts index 6becbc556dab6..25f385115b1e9 100644 --- a/packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuItemWrapper.types.ts +++ b/packages/react-next/src/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuItemWrapper.types.ts @@ -128,6 +128,7 @@ export interface IContextualMenuItemWrapperProps extends React.ClassAttributes void; /** @@ -139,5 +140,6 @@ export interface IContextualMenuItemWrapperProps extends React.ClassAttributes void; } From 906b61d5a4ed830ba0c5259e59d9ca6365fbe9b5 Mon Sep 17 00:00:00 2001 From: Michael Loughry Date: Mon, 10 Aug 2020 21:16:51 +0000 Subject: [PATCH 6/9] Use common-styles package --- .../react-next/src/common/DirectionalHint.ts | 75 ----- .../react-next/src/common/DocPage.types.ts | 163 ---------- .../src/common/IAccessiblePopupProps.ts | 39 --- packages/react-next/src/common/TestImages.ts | 18 -- .../react-next/src/common/ThemingSass.scss | 6 - packages/react-next/src/common/_common.scss | 10 - packages/react-next/src/common/_effects.scss | 8 - .../react-next/src/common/_focusBorder.scss | 63 ---- .../react-next/src/common/_highContrast.scss | 17 - packages/react-next/src/common/_i18n.scss | 295 ------------------ .../src/common/_legacyThemePalette.scss | 19 -- .../react-next/src/common/_semanticSlots.scss | 101 ------ .../src/common/_themeOverrides.scss | 51 --- .../src/common/_themeVariables.scss | 118 ------- .../src/common/shallowUntilTarget.ts | 68 ---- .../src/common/sharedIsConformant.ts | 10 - .../react-next/src/common/testUtilities.ts | 63 ---- .../examples/ContextualMenuExample.scss | 2 +- 18 files changed, 1 insertion(+), 1125 deletions(-) delete mode 100644 packages/react-next/src/common/DirectionalHint.ts delete mode 100644 packages/react-next/src/common/DocPage.types.ts delete mode 100644 packages/react-next/src/common/IAccessiblePopupProps.ts delete mode 100644 packages/react-next/src/common/TestImages.ts delete mode 100644 packages/react-next/src/common/ThemingSass.scss delete mode 100644 packages/react-next/src/common/_common.scss delete mode 100644 packages/react-next/src/common/_effects.scss delete mode 100644 packages/react-next/src/common/_focusBorder.scss delete mode 100644 packages/react-next/src/common/_highContrast.scss delete mode 100644 packages/react-next/src/common/_i18n.scss delete mode 100644 packages/react-next/src/common/_legacyThemePalette.scss delete mode 100644 packages/react-next/src/common/_semanticSlots.scss delete mode 100644 packages/react-next/src/common/_themeOverrides.scss delete mode 100644 packages/react-next/src/common/_themeVariables.scss delete mode 100644 packages/react-next/src/common/shallowUntilTarget.ts delete mode 100644 packages/react-next/src/common/sharedIsConformant.ts delete mode 100644 packages/react-next/src/common/testUtilities.ts diff --git a/packages/react-next/src/common/DirectionalHint.ts b/packages/react-next/src/common/DirectionalHint.ts deleted file mode 100644 index e8425bb8da3a0..0000000000000 --- a/packages/react-next/src/common/DirectionalHint.ts +++ /dev/null @@ -1,75 +0,0 @@ -export const DirectionalHint = { - /** - * Appear above the target element, with the left edges of the callout and target aligning. - */ - topLeftEdge: 0 as 0, - - /** - * Appear above the target element, with the centers of the callout and target aligning. - */ - topCenter: 1 as 1, - - /** - * Appear above the target element, with the right edges of the callout and target aligning. - */ - topRightEdge: 2 as 2, - - /** - * Appear above the target element, aligning with the target element such that the callout tends toward - * the center of the screen. - */ - topAutoEdge: 3 as 3, - - /** - * Appear below the target element, with the left edges of the callout and target aligning. - */ - bottomLeftEdge: 4 as 4, - - /** - * Appear below the target element, with the centers of the callout and target aligning. - */ - bottomCenter: 5 as 5, - - /** - * Appear below the target element, with the right edges of the callout and target aligning. - */ - bottomRightEdge: 6 as 6, - - /** - * Appear below the target element, aligning with the target element such that the callout tends toward - * the center of the screen. - */ - bottomAutoEdge: 7 as 7, - - /** - * Appear to the left of the target element, with the top edges of the callout and target aligning. - */ - leftTopEdge: 8 as 8, - - /** - * Appear to the left of the target element, with the centers of the callout and target aligning. - */ - leftCenter: 9 as 9, - - /** - * Appear to the left of the target element, with the bottom edges of the callout and target aligning. - */ - leftBottomEdge: 10 as 10, - - /** - * Appear to the right of the target element, with the top edges of the callout and target aligning. - */ - rightTopEdge: 11 as 11, - - /** - * Appear to the right of the target element, with the centers of the callout and target aligning. - */ - rightCenter: 12 as 12, - - /** - * Appear to the right of the target element, with the bottom edges of the callout and target aligning. - */ - rightBottomEdge: 13 as 13, -}; - -export type DirectionalHint = typeof DirectionalHint[keyof typeof DirectionalHint]; diff --git a/packages/react-next/src/common/DocPage.types.ts b/packages/react-next/src/common/DocPage.types.ts deleted file mode 100644 index f6aa6b0c0c638..0000000000000 --- a/packages/react-next/src/common/DocPage.types.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { IStyleFunctionOrObject, Omit } from '../Utilities'; -import { ITheme, IStyle } from '../Styling'; - -export interface IExample { - /** Title of the example */ - title: string; - - /** Raw source code of the example */ - code: string; - - /** Working example of the example */ - view: JSX.Element; - - isScrollable?: boolean; - - /** JS String for codepen of the example */ - codepenJS?: string; - - /** Custom styles. Partial version of `IExampleCardProps['styles']`. */ - styles?: IStyleFunctionOrObject<{ theme?: ITheme }, { root: IStyle }>; -} - -export interface IDocPageProps { - /** Title that goes into the header */ - title: string; - - /** Name of the component being documented */ - componentName: string; - - /** URL of the checked in component, should be somewhere on github.com */ - componentUrl: string; - - /** Knobs that applies to all the examples */ - exampleKnobs?: JSX.Element; - - /** Array of examples, displayed in the order defined */ - examples?: IExample[]; - - /** Properties table(s) as markdown string */ - propertiesTablesSources?: string[]; - - /** Overview of the component as markdown string */ - overview?: string; - - /** Accessibility of the component as markdown string */ - accessibility?: string; - - /** DO's blurb as markdown string */ - dos?: string; - - /** DON'Ts blurb as markdown string */ - donts?: string; - - /** Best practice as markdown string */ - bestPractices?: string; - - /** Feedback section includes link to new issue page and displays Github issues */ - isFeedbackVisible?: boolean; - - /** Passed through header visibility flag from the demo component page component */ - isHeaderVisible: boolean; - - /** If true, the component accepts all native props from elements specified in `nativePropsElement` */ - allowNativeProps?: boolean; - - /** Override component name to use in the native props message */ - allowNativePropsForComponentName?: string; - - /** - * Element(s) whose native props this component accepts (default div). - * Only relevant if `allowNativeProps` is true. - */ - nativePropsElement?: string | string[]; - - /** - * Related link - * @deprecated No longer shown on ComponentPage - */ - related?: JSX.Element; - - /** Pass through other sections for ComponentPage */ - otherSections?: { - title: string; - section: JSX.Element; - }[]; - - /** - * JSON to populate the api reference tables - */ - jsonDocs?: IPageJson; -} - -/** - * Used to keep track of where an API reference page will live on the site. - */ -export type PageKind = 'References' | 'Components'; - -/** - * Text excerpt token that is part of a type definition or extends block and may have a link - * to another doc page. For API reference tables. - */ -export interface ILinkToken { - text: string; - /** If this token is a link, name of the doc page it points to */ - linkedPage?: string; - /** If this token is a link, group/category of the doc page it points to */ - linkedPageGroup?: string; -} - -/** - * Generic row for API reference tables. - * It can represent a member (property or method) of an interface or class. - */ -export interface ITableRowJson { - name: string; - kind?: 'method' | 'property'; - /** - * The row's type translated to an array of text elements and links to other types. - * For example, `Readonly` would translate to: - * `[{ text: 'Readonly<' }, { text: 'IFoo', hyperlinkedPage: '(page name)', pageKind: '(kind)' }, { text: '>' }]` - */ - typeTokens: ILinkToken[]; - defaultValue?: string; - description: string; - deprecated: boolean; - deprecatedMessage?: string; - required?: boolean; -} - -/** - * Enum member row for API reference tables. - */ -export type IEnumTableRowJson = Omit & { - value: string; -}; - -export type ApiKind = 'interface' | 'enum' | 'class' | 'typeAlias'; - -/** - * Info for a table representing a top-level API item: interface, enum, class, or type alias. - */ -export interface ITableJson { - kind: ApiKind; - name: string; - /** - * Any types the item extends, translated to an array of text elements and links to other types. - * For classes and interfaces only. - */ - extendsTokens: ILinkToken[]; - description: string; - members?: ITableRowJson[] | IEnumTableRowJson[]; - deprecated?: boolean; - deprecatedMessage?: string; -} - -/** - * Structure of the page.json files - */ -export interface IPageJson { - tables: ITableJson[]; - name: string; - group?: string; -} diff --git a/packages/react-next/src/common/IAccessiblePopupProps.ts b/packages/react-next/src/common/IAccessiblePopupProps.ts deleted file mode 100644 index 8d010ca9def89..0000000000000 --- a/packages/react-next/src/common/IAccessiblePopupProps.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * {@docCategory IAccessiblePopupProps} - */ -export interface IAccessiblePopupProps { - /** - * Sets the element to focus on when exiting the control's FocusTrapZone. - * @defaultvalue The `element.target` that triggered the control opening. - */ - elementToFocusOnDismiss?: HTMLElement; - - /** - * If false (the default), the control's FocusTrapZone will restore focus to the element which - * activated it once the trap zone is unmounted or disabled. Set to true to disable this behavior. - * @defaultvalue false - */ - ignoreExternalFocusing?: boolean; - - /** - * Whether control should force focus inside its focus trap zone. - * @defaultvalue true - */ - forceFocusInsideTrap?: boolean; - - /** - * Class name (not actual selector) for first focusable item. Do not append a dot. - */ - firstFocusableSelector?: string | (() => string); - - /** - * Aria label on close button. - */ - closeButtonAriaLabel?: string; - - /** - * Whether this control will allow clicks outside its FocusTrapZone. - * @defaultvalue false - */ - isClickableOutsideFocusTrap?: boolean; -} diff --git a/packages/react-next/src/common/TestImages.ts b/packages/react-next/src/common/TestImages.ts deleted file mode 100644 index b0c19e972367c..0000000000000 --- a/packages/react-next/src/common/TestImages.ts +++ /dev/null @@ -1,18 +0,0 @@ -// If this file is moved or split, the scripts for building codepen examples will likely need to be updated. - -const baseProductionCdnUrl = 'https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/'; - -/** @deprecated Use the version from `@uifabric/example-data` instead. */ -export const TestImages = { - choiceGroupBarUnselected: baseProductionCdnUrl + 'choicegroup-bar-unselected.png', - choiceGroupBarSelected: baseProductionCdnUrl + 'choicegroup-bar-selected.png', - choiceGroupPieUnselected: baseProductionCdnUrl + 'choicegroup-pie-unselected.png', - choiceGroupPieSelected: baseProductionCdnUrl + 'choicegroup-pie-selected.png', - documentPreview: baseProductionCdnUrl + 'document-preview.png', - documentPreviewTwo: baseProductionCdnUrl + 'document-preview2.png', - documentPreviewThree: baseProductionCdnUrl + 'document-preview3.png', - iconOne: baseProductionCdnUrl + 'icon-one.png', - iconPpt: 'https://static2.sharepointonline.com/files/fabric/assets/item-types/32/pptx.png', - personaFemale: baseProductionCdnUrl + 'persona-female.png', - personaMale: baseProductionCdnUrl + 'persona-male.png', -}; diff --git a/packages/react-next/src/common/ThemingSass.scss b/packages/react-next/src/common/ThemingSass.scss deleted file mode 100644 index 00ee5e3fe3f52..0000000000000 --- a/packages/react-next/src/common/ThemingSass.scss +++ /dev/null @@ -1,6 +0,0 @@ -/** Imports all theming-related SASS files. */ -@import './effects'; -@import './legacyThemePalette'; -@import './semanticSlots'; -@import './themeOverrides'; -@import './themeVariables'; diff --git a/packages/react-next/src/common/_common.scss b/packages/react-next/src/common/_common.scss deleted file mode 100644 index 325b29dfa1eb3..0000000000000 --- a/packages/react-next/src/common/_common.scss +++ /dev/null @@ -1,10 +0,0 @@ -@import '~office-ui-fabric-core/dist/sass/References'; -@import './i18n'; -@import './themeOverrides'; -@import './focusBorder'; -@import './semanticSlots'; -@import './highContrast'; - -// Screen sizes that align with UHF breakpoints for the website -$uhf-screen-min-mobile: 768px; -$uhf-screen-max-mobile: ($uhf-screen-min-mobile - 1); diff --git a/packages/react-next/src/common/_effects.scss b/packages/react-next/src/common/_effects.scss deleted file mode 100644 index b42812353eae8..0000000000000 --- a/packages/react-next/src/common/_effects.scss +++ /dev/null @@ -1,8 +0,0 @@ -$elevation4: '[theme:elevation4, default: 0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)]'; -$elevation8: '[theme:elevation8, default: 0 3.2px 7.2px 0 rgba(0, 0, 0, 0.132), 0 0.6px 1.8px 0 rgba(0, 0, 0, 0.108)]'; -$elevation16: '[theme:elevation16, default: 0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)]'; -$elevation64: '[theme:elevation64, default: 0 25.6px 57.6px 0 rgba(0, 0, 0, 0.22), 0 4.8px 14.4px 0 rgba(0, 0, 0, 0.18)]'; - -$roundedCorner2: '[theme:roundedCorner2, default: 2px]'; -$roundedCorner4: '[theme:roundedCorner4, default: 4px]'; -$roundedCorner6: '[theme:roundedCorner6, default: 6px]'; diff --git a/packages/react-next/src/common/_focusBorder.scss b/packages/react-next/src/common/_focusBorder.scss deleted file mode 100644 index 3d6e2a5baa273..0000000000000 --- a/packages/react-next/src/common/_focusBorder.scss +++ /dev/null @@ -1,63 +0,0 @@ -@import './semanticSlots'; - -@mixin focus-clear() { - &::-moz-focus-inner { - // Clear the focus border in Firefox. Reference: http://stackoverflow.com/a/199319/1436671 - border: 0; - } - - & { - // Clear browser specific focus styles and use transparent as placeholder for focus style - outline: transparent; - } -} - -@mixin focus($onFocus: true) { - @if $onFocus { - :global(.ms-Fabric--isFocusVisible) &:focus { - @content; - } - } - @else { - @content; - } -} - -@mixin focus-border($padding: 0, $color: $focusBorderColor, $thickness: 1px, $onFocus: true, $position: relative) { - @include focus-clear(); - - & { - // It is MUST because the pseudo-element is absolute position. - position: $position; - } - - @include focus($onFocus) { - &:after { - @include after-outline($padding, $color, $thickness); - } - } -} - -// When focus is set using the keyboard, apply an outline. -@mixin after-outline ($padding: 0, $color: $focusBorderColor, $thickness: 1px) { - content: ''; - position: absolute; - top: $padding; - right: $padding; - bottom: $padding; - left: $padding; - - // Make the content not respond to mouse/touch event. Reference: https://css-tricks.com/almanac/properties/p/pointer-events/ - pointer-events: none; - - // Add focus border with $color - border: $thickness solid $color; -} - - -// When focus is set using the keyboard, apply an outline. -@mixin focus-outline { - :global(.ms-Fabric--isFocusVisible) &:focus { - outline: 1px solid $focusBorderColor; - } -} diff --git a/packages/react-next/src/common/_highContrast.scss b/packages/react-next/src/common/_highContrast.scss deleted file mode 100644 index 833eb712a288e..0000000000000 --- a/packages/react-next/src/common/_highContrast.scss +++ /dev/null @@ -1,17 +0,0 @@ -@mixin high-contrast { - @media screen and (-ms-high-contrast: active) { - @content; - } -} - -@mixin high-contrast-black-on-white { - @media screen and (-ms-high-contrast: black-on-white) { - @content; - } -} - -@mixin high-contrast-white-on-black { - @media screen and (-ms-high-contrast: white-on-black) { - @content; - } -} \ No newline at end of file diff --git a/packages/react-next/src/common/_i18n.scss b/packages/react-next/src/common/_i18n.scss deleted file mode 100644 index e7c5230f933bd..0000000000000 --- a/packages/react-next/src/common/_i18n.scss +++ /dev/null @@ -1,295 +0,0 @@ -// Some things copied from WinJS with <3 - - -// LTR mixin definition -@mixin LTR { - html[dir='ltr'] & { - @content; - } -} - -// RTL mixin definition -@mixin RTL { - html[dir='rtl'] & { - @content; - } -} - -// Use baseRTL for a root element of a control that needs rtl support -@mixin baseRtl { - @include RTL { - direction: rtl; - unicode-bidi: bidi-override; - } -} - -/* - Common CSS property mixins with support for RTL. - Use these mixins when you want to automatically create RTL versions of your properties. - They are in alphabetical order (a-z). -*/ - -@mixin border-color($top, $right, $bottom, $left) { - border-color: $top $right $bottom $left; - @include RTL { - border-color: $top $left $bottom $right; - } -} - -@mixin border-left($width, $style, $color) { - @include LTR { - border-left: $width $style $color; - } - @include RTL { - border-right: $width $style $color; - } -} - -@mixin border-left-color($color) { - @include LTR { - border-left-color: $color; - } - @include RTL { - border-right-color: $color; - } -} - -@mixin border-left-style($style) { - @include LTR { - border-left-style: $style; - } - @include RTL { - border-right-style: $style; - } -} - -@mixin border-left-width($width) { - @include LTR { - border-left-width: $width; - } - @include RTL { - border-right-width: $width; - } -} - -@mixin border-radius($topLeft, $topRight, $bottomRight, $bottomLeft) { - border-radius: $topLeft $topRight $bottomRight $bottomLeft; - @include RTL { - border-radius: $topRight $topLeft $bottomLeft $bottomRight; - } -} - -@mixin border-right($width, $style, $color) { - @include LTR { - border-right: $width $style $color; - } - @include RTL { - border-left: $width $style $color; - } -} - -@mixin border-right-color($color) { - @include LTR { - border-right-color: $color; - } - @include RTL { - border-left-color: $color; - } -} - -@mixin border-right-style($style) { - @include LTR { - border-right-style: $style; - } - @include RTL { - border-left-style: $style; - } -} - -@mixin border-right-width($width) { - @include LTR { - border-right-width: $width; - } - @include RTL { - border-left-width: $width; - } -} - -@mixin clear($side) { - @if $side == left { - @include LTR { - clear: $side; - } - @include RTL { - clear: right; - } - } @else if $side == right { - @include LTR { - clear: $side; - } - - @include RTL { - clear: left; - } - } @else { - clear: $side; - } -} - -@mixin float($direction) { - @if $direction == left { - @include LTR { - float: left; - } - @include RTL { - float: right; - } - } @else { - @include LTR { - float: right; - } - @include RTL { - float: left; - } - } -} - -@mixin left($distance) { - @include LTR { - left: $distance; - } - @include RTL { - right: $distance; - } -} - -@mixin margin($top, $right, $bottom, $left) { - margin: $top $right $bottom $left; - @include RTL { - margin: $top $left $bottom $right; - } -} - -@mixin margin-left($distance) { - @include LTR { - margin-left: $distance; - } - @include RTL { - margin-right: $distance; - } -} - -@mixin margin-right($distance) { - @include LTR { - margin-right: $distance; - } - @include RTL { - margin-left: $distance; - } -} - -@mixin padding($top, $right, $bottom, $left) { - padding: $top $right $bottom $left; - @include RTL { - padding: $top $left $bottom $right; - } -} - -@mixin padding-left($distance) { - @include LTR { - padding-left: $distance; - } - @include RTL { - padding-right: $distance; - } -} - -@mixin padding-right($distance) { - @include LTR { - padding-right: $distance; - } - @include RTL { - padding-left: $distance; - } -} - -@mixin right($distance) { - @include LTR { - right: $distance; - } - @include RTL { - left: $distance; - } -} - -@mixin text-align($direction) { - @if $direction == left { - @include LTR { - text-align: left; - } - @include RTL { - text-align: right; - } - } @else { - @include LTR { - text-align: right; - } - @include RTL { - text-align: left; - } - } -} - -@mixin box-shadow($left, $etc) { - @include LTR { - box-shadow: $left $etc; - } - - @include RTL { - box-shadow: -$left $etc; - } -} - -@mixin transform-scaleX($scaleX: 1) { - @include LTR { - transform: scaleX($scaleX); - } - @include RTL { - transform: scaleX(-$scaleX); - } -} - -@mixin transform-translateX($distance) { - @include LTR { - transform: translateX($distance); - } - @include RTL { - transform: translateX(-$distance); - } -} - -// only supported when ONLY left/right are declared -@mixin transition-property($direction) { - @if $direction == left { - @include LTR { - transition-property: left; - } - @include RTL { - transition-property: right; - } - } @else { - @include LTR { - transition-property: right; - } - @include RTL { - transition-property: left; - } - } -} - -// Disables high contrast color adjusts for Edge/IE11 -@mixin highContrastAdjust { - @media screen and (-ms-high-contrast: active), screen and (-ms-high-contrast: black-on-white) { - -ms-high-contrast-adjust: none; - } -} \ No newline at end of file diff --git a/packages/react-next/src/common/_legacyThemePalette.scss b/packages/react-next/src/common/_legacyThemePalette.scss deleted file mode 100644 index 003bc3bcf5766..0000000000000 --- a/packages/react-next/src/common/_legacyThemePalette.scss +++ /dev/null @@ -1,19 +0,0 @@ -$ms-color-error: "[theme:error, default: #a80000]"; -$ms-color-errorText: "[theme:warningText, default: #333333]"; -$ms-color-errorBackground: "[theme:errorBackground, default: rgba(232,17,35,.2)]"; -$ms-color-success: "[theme:success, default: #107c10]"; -$ms-color-successText: "[theme:successText, default: #333333]"; -$ms-color-successBackground: "[theme:successBackground, default: #dff6dd]"; -$ms-color-alert: "[theme:alert, default: #d83b01]"; -$ms-color-alertText: "[theme:alertText, default: #333333]"; -$ms-color-alertBackground: "[theme:alertBackground, default: #deecf9]"; -$ms-color-warning: "[theme:warning, default: #767676]"; -$ms-color-warningText: "[theme:warningText, default: #333333]"; -$ms-color-warningBackground: "[theme:warningBackground, default: #fff4ce]"; -$ms-color-severeWarning: "[theme:severeWarning, default: #a80000]"; -$ms-color-severeWarningText: "[theme:severeWarningText, default: #333333]"; -$ms-color-severeWarningBackground: "[theme:severeWarningBackground, default: #fed9cc]"; -$ms-color-info: "[theme:info, default: #666666]"; -$ms-color-infoText: "[theme:infoText, default: #333333]"; -$ms-color-infoBackground: "[theme:infoBackground, default: #f4f4f4]"; -$ms-color-themeAccent: "[theme:accent, default:#0078d4]"; \ No newline at end of file diff --git a/packages/react-next/src/common/_semanticSlots.scss b/packages/react-next/src/common/_semanticSlots.scss deleted file mode 100644 index 3341c5c1f7c80..0000000000000 --- a/packages/react-next/src/common/_semanticSlots.scss +++ /dev/null @@ -1,101 +0,0 @@ -/** THIS FILE IS AUTOGENERATED do not modify it manually. See generateDefaultThemeSassFiles.js. New slots should be added to the appropriate interfaces and defaults files. */ -$bodyBackgroundColor: '[theme:bodyBackground, default: #ffffff]'; -$bodyBackgroundHoveredColor: '[theme:bodyBackgroundHovered, default: #f3f2f1]'; -$bodyBackgroundCheckedColor: '[theme:bodyBackgroundChecked, default: #edebe9]'; -$bodyStandoutBackgroundColor: '[theme:bodyStandoutBackground, default: #faf9f8]'; -$bodyFrameBackgroundColor: '[theme:bodyFrameBackground, default: #ffffff]'; -$bodyFrameDividerColor: '[theme:bodyFrameDivider, default: #edebe9]'; -$bodyTextColor: '[theme:bodyText, default: #323130]'; -$bodyTextCheckedColor: '[theme:bodyTextChecked, default: #000000]'; -$bodySubtextColor: '[theme:bodySubtext, default: #605e5c]'; -$bodyDividerColor: '[theme:bodyDivider, default: #edebe9]'; -$disabledBodyTextColor: '[theme:disabledBodyText, default: #a19f9d]'; -$disabledBodySubtextColor: '[theme:disabledBodySubtext, default: #c8c6c4]'; -$disabledBorderColor: '[theme:disabledBorder, default: #c8c6c4]'; -$focusBorderColor: '[theme:focusBorder, default: #605e5c]'; -$variantBorderColor: '[theme:variantBorder, default: #edebe9]'; -$variantBorderHoveredColor: '[theme:variantBorderHovered, default: #a19f9d]'; -$defaultStateBackgroundColor: '[theme:defaultStateBackground, default: #faf9f8]'; -$actionLinkColor: '[theme:actionLink, default: #323130]'; -$actionLinkHoveredColor: '[theme:actionLinkHovered, default: #201f1e]'; -$linkColor: '[theme:link, default: #0078d4]'; -$linkHoveredColor: '[theme:linkHovered, default: #004578]'; -$buttonBackgroundColor: '[theme:buttonBackground, default: #ffffff]'; -$buttonBackgroundCheckedColor: '[theme:buttonBackgroundChecked, default: #c8c6c4]'; -$buttonBackgroundHoveredColor: '[theme:buttonBackgroundHovered, default: #f3f2f1]'; -$buttonBackgroundCheckedHoveredColor: '[theme:buttonBackgroundCheckedHovered, default: #edebe9]'; -$buttonBackgroundPressedColor: '[theme:buttonBackgroundPressed, default: #edebe9]'; -$buttonBackgroundDisabledColor: '[theme:buttonBackgroundDisabled, default: #f3f2f1]'; -$buttonBorderColor: '[theme:buttonBorder, default: #8a8886]'; -$buttonTextColor: '[theme:buttonText, default: #323130]'; -$buttonTextHoveredColor: '[theme:buttonTextHovered, default: #201f1e]'; -$buttonTextCheckedColor: '[theme:buttonTextChecked, default: #201f1e]'; -$buttonTextCheckedHoveredColor: '[theme:buttonTextCheckedHovered, default: #000000]'; -$buttonTextPressedColor: '[theme:buttonTextPressed, default: #201f1e]'; -$buttonTextDisabledColor: '[theme:buttonTextDisabled, default: #a19f9d]'; -$buttonBorderDisabledColor: '[theme:buttonBorderDisabled, default: #f3f2f1]'; -$primaryButtonBackgroundColor: '[theme:primaryButtonBackground, default: #0078d4]'; -$primaryButtonBackgroundHoveredColor: '[theme:primaryButtonBackgroundHovered, default: #106ebe]'; -$primaryButtonBackgroundPressedColor: '[theme:primaryButtonBackgroundPressed, default: #005a9e]'; -$primaryButtonBackgroundDisabledColor: '[theme:primaryButtonBackgroundDisabled, default: #f3f2f1]'; -$primaryButtonBorderColor: '[theme:primaryButtonBorder, default: transparent]'; -$primaryButtonTextColor: '[theme:primaryButtonText, default: #ffffff]'; -$primaryButtonTextHoveredColor: '[theme:primaryButtonTextHovered, default: #ffffff]'; -$primaryButtonTextPressedColor: '[theme:primaryButtonTextPressed, default: #ffffff]'; -$primaryButtonTextDisabledColor: '[theme:primaryButtonTextDisabled, default: #d2d0ce]'; -$accentButtonBackgroundColor: '[theme:accentButtonBackground, default: #0078d4]'; -$accentButtonTextColor: '[theme:accentButtonText, default: #ffffff]'; -$inputBorderColor: '[theme:inputBorder, default: #605e5c]'; -$inputBorderHoveredColor: '[theme:inputBorderHovered, default: #323130]'; -$inputBackgroundColor: '[theme:inputBackground, default: #ffffff]'; -$inputBackgroundCheckedColor: '[theme:inputBackgroundChecked, default: #0078d4]'; -$inputBackgroundCheckedHoveredColor: '[theme:inputBackgroundCheckedHovered, default: #005a9e]'; -$inputPlaceholderBackgroundCheckedColor: '[theme:inputPlaceholderBackgroundChecked, default: #deecf9]'; -$inputForegroundCheckedColor: '[theme:inputForegroundChecked, default: #ffffff]'; -$inputIconColor: '[theme:inputIcon, default: #0078d4]'; -$inputIconHoveredColor: '[theme:inputIconHovered, default: #005a9e]'; -$inputIconDisabledColor: '[theme:inputIconDisabled, default: #a19f9d]'; -$inputFocusBorderAltColor: '[theme:inputFocusBorderAlt, default: #0078d4]'; -$smallInputBorderColor: '[theme:smallInputBorder, default: #605e5c]'; -$inputTextColor: '[theme:inputText, default: #323130]'; -$inputTextHoveredColor: '[theme:inputTextHovered, default: #201f1e]'; -$inputPlaceholderTextColor: '[theme:inputPlaceholderText, default: #605e5c]'; -$disabledBackgroundColor: '[theme:disabledBackground, default: #f3f2f1]'; -$disabledTextColor: '[theme:disabledText, default: #a19f9d]'; -$disabledSubtextColor: '[theme:disabledSubtext, default: #d2d0ce]'; -$listBackgroundColor: '[theme:listBackground, default: #ffffff]'; -$listTextColor: '[theme:listText, default: #323130]'; -$listItemBackgroundHoveredColor: '[theme:listItemBackgroundHovered, default: #f3f2f1]'; -$listItemBackgroundCheckedColor: '[theme:listItemBackgroundChecked, default: #edebe9]'; -$listItemBackgroundCheckedHoveredColor: '[theme:listItemBackgroundCheckedHovered, default: #e1dfdd]'; -$listHeaderBackgroundHoveredColor: '[theme:listHeaderBackgroundHovered, default: #f3f2f1]'; -$listHeaderBackgroundPressedColor: '[theme:listHeaderBackgroundPressed, default: #edebe9]'; -$menuBackgroundColor: '[theme:menuBackground, default: #ffffff]'; -$menuDividerColor: '[theme:menuDivider, default: #c8c6c4]'; -$menuIconColor: '[theme:menuIcon, default: #0078d4]'; -$menuHeaderColor: '[theme:menuHeader, default: #0078d4]'; -$menuItemBackgroundHoveredColor: '[theme:menuItemBackgroundHovered, default: #f3f2f1]'; -$menuItemBackgroundPressedColor: '[theme:menuItemBackgroundPressed, default: #edebe9]'; -$menuItemTextColor: '[theme:menuItemText, default: #323130]'; -$menuItemTextHoveredColor: '[theme:menuItemTextHovered, default: #201f1e]'; -$errorTextColor: '[theme:errorText, default: #a4262c]'; -$messageTextColor: '[theme:messageText, default: #323130]'; -$messageLinkColor: '[theme:messageLink, default: #005A9E]'; -$messageLinkHoveredColor: '[theme:messageLinkHovered, default: #004578]'; -$infoIconColor: '[theme:infoIcon, default: #605e5c]'; -$errorIconColor: '[theme:errorIcon, default: #A80000]'; -$blockingIconColor: '[theme:blockingIcon, default: #FDE7E9]'; -$warningIconColor: '[theme:warningIcon, default: #797775]'; -$severeWarningIconColor: '[theme:severeWarningIcon, default: #D83B01]'; -$successIconColor: '[theme:successIcon, default: #107C10]'; -$infoBackgroundColor: '[theme:infoBackground, default: #f3f2f1]'; -$errorBackgroundColor: '[theme:errorBackground, default: #FDE7E9]'; -$blockingBackgroundColor: '[theme:blockingBackground, default: #FDE7E9]'; -$warningBackgroundColor: '[theme:warningBackground, default: #FFF4CE]'; -$severeWarningBackgroundColor: '[theme:severeWarningBackground, default: #FED9CC]'; -$successBackgroundColor: '[theme:successBackground, default: #DFF6DD]'; -$warningHighlightColor: '[theme:warningHighlight, default: #ffb900]'; /* @deprecated */ -$warningTextColor: '[theme:warningText, default: #323130]'; /* @deprecated */ -$successTextColor: '[theme:successText, default: #107C10]'; /* @deprecated */ -$listTextColorColor: '[theme:listTextColor, default: #323130]'; /* @deprecated */ -$menuItemBackgroundCheckedColor: '[theme:menuItemBackgroundChecked, default: #edebe9]'; /* @deprecated */ \ No newline at end of file diff --git a/packages/react-next/src/common/_themeOverrides.scss b/packages/react-next/src/common/_themeOverrides.scss deleted file mode 100644 index 5f46f913fdbce..0000000000000 --- a/packages/react-next/src/common/_themeOverrides.scss +++ /dev/null @@ -1,51 +0,0 @@ -/** THIS FILE IS AUTOGENERATED do not modify it manually. See generateDefaultThemeSassFiles.js. New slots should be added to the appropriate interfaces and defaults files. */ -$ms-color-themeDarker: "[theme:themeDarker, default: #004578]"; -$ms-color-themeDark: "[theme:themeDark, default: #005a9e]"; -$ms-color-themeDarkAlt: "[theme:themeDarkAlt, default: #106ebe]"; -$ms-color-themePrimary: "[theme:themePrimary, default: #0078d4]"; -$ms-color-themeSecondary: "[theme:themeSecondary, default: #2b88d8]"; -$ms-color-themeTertiary: "[theme:themeTertiary, default: #71afe5]"; -$ms-color-themeLight: "[theme:themeLight, default: #c7e0f4]"; -$ms-color-themeLighter: "[theme:themeLighter, default: #deecf9]"; -$ms-color-themeLighterAlt: "[theme:themeLighterAlt, default: #eff6fc]"; -$ms-color-black: "[theme:black, default: #000000]"; -$ms-color-blackTranslucent40: "[theme:blackTranslucent40, default: rgba(0,0,0,.4)]"; -$ms-color-neutralDark: "[theme:neutralDark, default: #201f1e]"; -$ms-color-neutralPrimary: "[theme:neutralPrimary, default: #323130]"; -$ms-color-neutralPrimaryAlt: "[theme:neutralPrimaryAlt, default: #3b3a39]"; -$ms-color-neutralSecondary: "[theme:neutralSecondary, default: #605e5c]"; -$ms-color-neutralSecondaryAlt: "[theme:neutralSecondaryAlt, default: #8a8886]"; -$ms-color-neutralTertiary: "[theme:neutralTertiary, default: #a19f9d]"; -$ms-color-neutralTertiaryAlt: "[theme:neutralTertiaryAlt, default: #c8c6c4]"; -$ms-color-neutralQuaternary: "[theme:neutralQuaternary, default: #d2d0ce]"; -$ms-color-neutralQuaternaryAlt: "[theme:neutralQuaternaryAlt, default: #e1dfdd]"; -$ms-color-neutralLight: "[theme:neutralLight, default: #edebe9]"; -$ms-color-neutralLighter: "[theme:neutralLighter, default: #f3f2f1]"; -$ms-color-neutralLighterAlt: "[theme:neutralLighterAlt, default: #faf9f8]"; -$ms-color-accent: "[theme:accent, default: #0078d4]"; -$ms-color-white: "[theme:white, default: #ffffff]"; -$ms-color-whiteTranslucent40: "[theme:whiteTranslucent40, default: rgba(255,255,255,.4)]"; -$ms-color-yellowDark: "[theme:yellowDark, default: #d29200]"; -$ms-color-yellow: "[theme:yellow, default: #ffb900]"; -$ms-color-yellowLight: "[theme:yellowLight, default: #fff100]"; -$ms-color-orange: "[theme:orange, default: #d83b01]"; -$ms-color-orangeLight: "[theme:orangeLight, default: #ea4300]"; -$ms-color-orangeLighter: "[theme:orangeLighter, default: #ff8c00]"; -$ms-color-redDark: "[theme:redDark, default: #a4262c]"; -$ms-color-red: "[theme:red, default: #e81123]"; -$ms-color-magentaDark: "[theme:magentaDark, default: #5c005c]"; -$ms-color-magenta: "[theme:magenta, default: #b4009e]"; -$ms-color-magentaLight: "[theme:magentaLight, default: #e3008c]"; -$ms-color-purpleDark: "[theme:purpleDark, default: #32145a]"; -$ms-color-purple: "[theme:purple, default: #5c2d91]"; -$ms-color-purpleLight: "[theme:purpleLight, default: #b4a0ff]"; -$ms-color-blueDark: "[theme:blueDark, default: #002050]"; -$ms-color-blueMid: "[theme:blueMid, default: #00188f]"; -$ms-color-blue: "[theme:blue, default: #0078d4]"; -$ms-color-blueLight: "[theme:blueLight, default: #00bcf2]"; -$ms-color-tealDark: "[theme:tealDark, default: #004b50]"; -$ms-color-teal: "[theme:teal, default: #008272]"; -$ms-color-tealLight: "[theme:tealLight, default: #00b294]"; -$ms-color-greenDark: "[theme:greenDark, default: #004b1c]"; -$ms-color-green: "[theme:green, default: #107c10]"; -$ms-color-greenLight: "[theme:greenLight, default: #bad80a]"; \ No newline at end of file diff --git a/packages/react-next/src/common/_themeVariables.scss b/packages/react-next/src/common/_themeVariables.scss deleted file mode 100644 index 2827cf95bc789..0000000000000 --- a/packages/react-next/src/common/_themeVariables.scss +++ /dev/null @@ -1,118 +0,0 @@ -/** THIS FILE IS AUTOGENERATED do not modify it manually. See generateDefaultThemeSassFiles.js. New slots should be added to the appropriate interfaces and defaults files. */ -$ms-font-tinyFontFamily: "[theme:tinyFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; -$ms-font-tinyMozOsxFontSmoothing: "[theme:tinyMozOsxFontSmoothing, default: grayscale]"; -$ms-font-tinyWebkitFontSmoothing: "[theme:tinyWebkitFontSmoothing, default: antialiased]"; -$ms-font-tinyFontSize: "[theme:tinyFontSize, default: 10px]"; -$ms-font-tinyFontWeight: "[theme:tinyFontWeight, default: 400]"; -@mixin tinyFontBasic { - font-size: $ms-font-tinyFontSize; - font-weight: $ms-font-tinyFontWeight; -} -$ms-font-xSmallFontFamily: "[theme:xSmallFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; -$ms-font-xSmallMozOsxFontSmoothing: "[theme:xSmallMozOsxFontSmoothing, default: grayscale]"; -$ms-font-xSmallWebkitFontSmoothing: "[theme:xSmallWebkitFontSmoothing, default: antialiased]"; -$ms-font-xSmallFontSize: "[theme:xSmallFontSize, default: 10px]"; -$ms-font-xSmallFontWeight: "[theme:xSmallFontWeight, default: 400]"; -@mixin xSmallFontBasic { - font-size: $ms-font-xSmallFontSize; - font-weight: $ms-font-xSmallFontWeight; -} -$ms-font-smallFontFamily: "[theme:smallFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; -$ms-font-smallMozOsxFontSmoothing: "[theme:smallMozOsxFontSmoothing, default: grayscale]"; -$ms-font-smallWebkitFontSmoothing: "[theme:smallWebkitFontSmoothing, default: antialiased]"; -$ms-font-smallFontSize: "[theme:smallFontSize, default: 12px]"; -$ms-font-smallFontWeight: "[theme:smallFontWeight, default: 400]"; -@mixin smallFontBasic { - font-size: $ms-font-smallFontSize; - font-weight: $ms-font-smallFontWeight; -} -$ms-font-smallPlusFontFamily: "[theme:smallPlusFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; -$ms-font-smallPlusMozOsxFontSmoothing: "[theme:smallPlusMozOsxFontSmoothing, default: grayscale]"; -$ms-font-smallPlusWebkitFontSmoothing: "[theme:smallPlusWebkitFontSmoothing, default: antialiased]"; -$ms-font-smallPlusFontSize: "[theme:smallPlusFontSize, default: 12px]"; -$ms-font-smallPlusFontWeight: "[theme:smallPlusFontWeight, default: 400]"; -@mixin smallPlusFontBasic { - font-size: $ms-font-smallPlusFontSize; - font-weight: $ms-font-smallPlusFontWeight; -} -$ms-font-mediumFontFamily: "[theme:mediumFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; -$ms-font-mediumMozOsxFontSmoothing: "[theme:mediumMozOsxFontSmoothing, default: grayscale]"; -$ms-font-mediumWebkitFontSmoothing: "[theme:mediumWebkitFontSmoothing, default: antialiased]"; -$ms-font-mediumFontSize: "[theme:mediumFontSize, default: 14px]"; -$ms-font-mediumFontWeight: "[theme:mediumFontWeight, default: 400]"; -@mixin mediumFontBasic { - font-size: $ms-font-mediumFontSize; - font-weight: $ms-font-mediumFontWeight; -} -$ms-font-mediumPlusFontFamily: "[theme:mediumPlusFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; -$ms-font-mediumPlusMozOsxFontSmoothing: "[theme:mediumPlusMozOsxFontSmoothing, default: grayscale]"; -$ms-font-mediumPlusWebkitFontSmoothing: "[theme:mediumPlusWebkitFontSmoothing, default: antialiased]"; -$ms-font-mediumPlusFontSize: "[theme:mediumPlusFontSize, default: 16px]"; -$ms-font-mediumPlusFontWeight: "[theme:mediumPlusFontWeight, default: 400]"; -@mixin mediumPlusFontBasic { - font-size: $ms-font-mediumPlusFontSize; - font-weight: $ms-font-mediumPlusFontWeight; -} -$ms-font-largeFontFamily: "[theme:largeFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; -$ms-font-largeMozOsxFontSmoothing: "[theme:largeMozOsxFontSmoothing, default: grayscale]"; -$ms-font-largeWebkitFontSmoothing: "[theme:largeWebkitFontSmoothing, default: antialiased]"; -$ms-font-largeFontSize: "[theme:largeFontSize, default: 18px]"; -$ms-font-largeFontWeight: "[theme:largeFontWeight, default: 400]"; -@mixin largeFontBasic { - font-size: $ms-font-largeFontSize; - font-weight: $ms-font-largeFontWeight; -} -$ms-font-xLargeFontFamily: "[theme:xLargeFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; -$ms-font-xLargeMozOsxFontSmoothing: "[theme:xLargeMozOsxFontSmoothing, default: grayscale]"; -$ms-font-xLargeWebkitFontSmoothing: "[theme:xLargeWebkitFontSmoothing, default: antialiased]"; -$ms-font-xLargeFontSize: "[theme:xLargeFontSize, default: 20px]"; -$ms-font-xLargeFontWeight: "[theme:xLargeFontWeight, default: 600]"; -@mixin xLargeFontBasic { - font-size: $ms-font-xLargeFontSize; - font-weight: $ms-font-xLargeFontWeight; -} -$ms-font-xLargePlusFontFamily: "[theme:xLargePlusFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; -$ms-font-xLargePlusMozOsxFontSmoothing: "[theme:xLargePlusMozOsxFontSmoothing, default: grayscale]"; -$ms-font-xLargePlusWebkitFontSmoothing: "[theme:xLargePlusWebkitFontSmoothing, default: antialiased]"; -$ms-font-xLargePlusFontSize: "[theme:xLargePlusFontSize, default: 24px]"; -$ms-font-xLargePlusFontWeight: "[theme:xLargePlusFontWeight, default: 600]"; -@mixin xLargePlusFontBasic { - font-size: $ms-font-xLargePlusFontSize; - font-weight: $ms-font-xLargePlusFontWeight; -} -$ms-font-xxLargeFontFamily: "[theme:xxLargeFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; -$ms-font-xxLargeMozOsxFontSmoothing: "[theme:xxLargeMozOsxFontSmoothing, default: grayscale]"; -$ms-font-xxLargeWebkitFontSmoothing: "[theme:xxLargeWebkitFontSmoothing, default: antialiased]"; -$ms-font-xxLargeFontSize: "[theme:xxLargeFontSize, default: 28px]"; -$ms-font-xxLargeFontWeight: "[theme:xxLargeFontWeight, default: 600]"; -@mixin xxLargeFontBasic { - font-size: $ms-font-xxLargeFontSize; - font-weight: $ms-font-xxLargeFontWeight; -} -$ms-font-xxLargePlusFontFamily: "[theme:xxLargePlusFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; -$ms-font-xxLargePlusMozOsxFontSmoothing: "[theme:xxLargePlusMozOsxFontSmoothing, default: grayscale]"; -$ms-font-xxLargePlusWebkitFontSmoothing: "[theme:xxLargePlusWebkitFontSmoothing, default: antialiased]"; -$ms-font-xxLargePlusFontSize: "[theme:xxLargePlusFontSize, default: 32px]"; -$ms-font-xxLargePlusFontWeight: "[theme:xxLargePlusFontWeight, default: 600]"; -@mixin xxLargePlusFontBasic { - font-size: $ms-font-xxLargePlusFontSize; - font-weight: $ms-font-xxLargePlusFontWeight; -} -$ms-font-superLargeFontFamily: "[theme:superLargeFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; -$ms-font-superLargeMozOsxFontSmoothing: "[theme:superLargeMozOsxFontSmoothing, default: grayscale]"; -$ms-font-superLargeWebkitFontSmoothing: "[theme:superLargeWebkitFontSmoothing, default: antialiased]"; -$ms-font-superLargeFontSize: "[theme:superLargeFontSize, default: 42px]"; -$ms-font-superLargeFontWeight: "[theme:superLargeFontWeight, default: 600]"; -@mixin superLargeFontBasic { - font-size: $ms-font-superLargeFontSize; - font-weight: $ms-font-superLargeFontWeight; -} -$ms-font-megaFontFamily: "[theme:megaFontFamily, default: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif]"; -$ms-font-megaMozOsxFontSmoothing: "[theme:megaMozOsxFontSmoothing, default: grayscale]"; -$ms-font-megaWebkitFontSmoothing: "[theme:megaWebkitFontSmoothing, default: antialiased]"; -$ms-font-megaFontSize: "[theme:megaFontSize, default: 68px]"; -$ms-font-megaFontWeight: "[theme:megaFontWeight, default: 600]"; -@mixin megaFontBasic { - font-size: $ms-font-megaFontSize; - font-weight: $ms-font-megaFontWeight; -} \ No newline at end of file diff --git a/packages/react-next/src/common/shallowUntilTarget.ts b/packages/react-next/src/common/shallowUntilTarget.ts deleted file mode 100644 index 4641925afbf68..0000000000000 --- a/packages/react-next/src/common/shallowUntilTarget.ts +++ /dev/null @@ -1,68 +0,0 @@ -import * as React from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; - -/** - * Duplicated enzyme's ShallowRendererProps - * - * @internal - */ -export interface IShallowRendererProps { - lifecycleExperimental?: boolean; - disableLifecycleMethods?: boolean; -} - -/** - * ShallowUntilTarget Interface - * - * @internal - */ -export interface IShallowUntilTarget { - maxTries: number; - shallowOptions: IShallowRendererProps; -} - -/** - * An extention of enzyme's shallow function which will fail to work - * with decorated components and/or components using the styled() function. - * This function allows you to pass a 'target' component (e.g. ComponentBase) - * and keep running shallow on each child component till a match is found. - * - * @public - */ -export function shallowUntilTarget( - componentInstance: React.ReactElement

    , - TargetComponent: string, - options: IShallowUntilTarget = { - maxTries: 10, - shallowOptions: {}, - }, -): ShallowWrapper { - const { maxTries, shallowOptions } = options; - - let root = shallow(componentInstance, shallowOptions); - let rootType = root.type(); - - if (typeof rootType === 'string' || rootType.toString().indexOf(TargetComponent) !== -1) { - // Default shallow() - // If type() is a string then it's a DOM Node. - // If it were wrapped, it would be a React component. - return root; - } - - for (let tries = 1; tries <= maxTries; tries++) { - // Check for target as a string to avoid conflicts - // with decoratored components name - if (rootType.toString().indexOf(TargetComponent) !== -1) { - // Now that we found the target component, render it. - return root.first().shallow(shallowOptions); - } - // Unwrap the next component in the hierarchy. - root = root.first().shallow(shallowOptions); - rootType = root.type(); - } - - throw new Error( - `Could not find ${TargetComponent} in React instance: ${componentInstance}; - gave up after ${maxTries} tries`, - ); -} diff --git a/packages/react-next/src/common/sharedIsConformant.ts b/packages/react-next/src/common/sharedIsConformant.ts deleted file mode 100644 index 87164d38f24e9..0000000000000 --- a/packages/react-next/src/common/sharedIsConformant.ts +++ /dev/null @@ -1,10 +0,0 @@ -// eslint-disable-next-line import/no-extraneous-dependencies -- this file is for testing -import { isConformant, IsConformantOptions } from '@fluentui/react-conformance'; - -export function sharedIsConformant(testInfo: IsConformantOptions) { - const defaultOptions = { - disabledTests: ['has-docblock'], - }; - - isConformant(defaultOptions, testInfo); -} diff --git a/packages/react-next/src/common/testUtilities.ts b/packages/react-next/src/common/testUtilities.ts deleted file mode 100644 index 81de215e56d85..0000000000000 --- a/packages/react-next/src/common/testUtilities.ts +++ /dev/null @@ -1,63 +0,0 @@ -import * as React from 'react'; -import { ReactWrapper, mount } from 'enzyme'; -import { Component } from 'react'; -import * as ReactDOM from 'react-dom'; -import * as ReactTestUtils from 'react-dom/test-utils'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function findNodes(wrapper: ReactWrapper, className: string): ReactWrapper { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return wrapper.find(className).filterWhere((node: ReactWrapper) => typeof node.type() === 'string'); -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function expectNodes(wrapper: ReactWrapper, className: string, n: number): void { - expect(findNodes(wrapper, className).length).toEqual(n); -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function expectOne(wrapper: ReactWrapper, className: string): void { - expectNodes(wrapper, className, 1); -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function expectMissing(wrapper: ReactWrapper, className: string): void { - expectNodes(wrapper, className, 0); -} - -/** @deprecated Use fake timers and `jest.runAllTimers()` instead */ -export function delay(millisecond: number): Promise { - return new Promise(resolve => setTimeout(resolve, millisecond)); -} - -/** - * Mounts the element attached to a child of document.body. This is primarily for tests involving - * event handlers (which don't work right unless the element is attached). - */ -export function mountAttached( - element: React.ReactElement

    , -): ReactWrapper { - const parent = document.createElement('div'); - document.body.appendChild(parent); - return mount(element, { attachTo: parent }); -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function renderIntoDocument(element: React.ReactElement): HTMLElement { - const component = ReactTestUtils.renderIntoDocument(element); - const renderedDOM = ReactDOM.findDOMNode(component as React.ReactInstance); - return renderedDOM as HTMLElement; -} - -export function mockEvent(targetValue: string = ''): ReactTestUtils.SyntheticEventData { - const target: EventTarget = { value: targetValue } as HTMLInputElement; - return { target }; -} - -/** - * Hack for forcing Jest to run pending promises - * https://github.com/facebook/jest/issues/2157#issuecomment-279171856 - */ -export function flushPromises() { - return new Promise(resolve => setImmediate(resolve)); -} diff --git a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenuExample.scss b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenuExample.scss index be13c717ca5c0..5b7b7db15cb0c 100644 --- a/packages/react-next/src/components/ContextualMenu/examples/ContextualMenuExample.scss +++ b/packages/react-next/src/components/ContextualMenu/examples/ContextualMenuExample.scss @@ -1,4 +1,4 @@ -@import '../../../common/common'; +@import '~@fluentui/common-styles/dist/sass/ThemingSass'; :global { .ms-ContextualMenuDirectionalExample { From de047506b5e10425e18c2b63875d21e73e3fe427 Mon Sep 17 00:00:00 2001 From: Michael Loughry Date: Mon, 10 Aug 2020 21:36:02 +0000 Subject: [PATCH 7/9] fIX BUILD --- .../react-next/src/common/DirectionalHint.ts | 75 ++++++++ .../react-next/src/common/DocPage.types.ts | 163 ++++++++++++++++++ .../src/common/IAccessiblePopupProps.ts | 39 +++++ packages/react-next/src/common/TestImages.ts | 18 ++ .../src/common/shallowUntilTarget.ts | 68 ++++++++ .../src/common/sharedIsConformant.ts | 10 ++ .../react-next/src/common/testUtilities.ts | 63 +++++++ 7 files changed, 436 insertions(+) create mode 100644 packages/react-next/src/common/DirectionalHint.ts create mode 100644 packages/react-next/src/common/DocPage.types.ts create mode 100644 packages/react-next/src/common/IAccessiblePopupProps.ts create mode 100644 packages/react-next/src/common/TestImages.ts create mode 100644 packages/react-next/src/common/shallowUntilTarget.ts create mode 100644 packages/react-next/src/common/sharedIsConformant.ts create mode 100644 packages/react-next/src/common/testUtilities.ts diff --git a/packages/react-next/src/common/DirectionalHint.ts b/packages/react-next/src/common/DirectionalHint.ts new file mode 100644 index 0000000000000..e8425bb8da3a0 --- /dev/null +++ b/packages/react-next/src/common/DirectionalHint.ts @@ -0,0 +1,75 @@ +export const DirectionalHint = { + /** + * Appear above the target element, with the left edges of the callout and target aligning. + */ + topLeftEdge: 0 as 0, + + /** + * Appear above the target element, with the centers of the callout and target aligning. + */ + topCenter: 1 as 1, + + /** + * Appear above the target element, with the right edges of the callout and target aligning. + */ + topRightEdge: 2 as 2, + + /** + * Appear above the target element, aligning with the target element such that the callout tends toward + * the center of the screen. + */ + topAutoEdge: 3 as 3, + + /** + * Appear below the target element, with the left edges of the callout and target aligning. + */ + bottomLeftEdge: 4 as 4, + + /** + * Appear below the target element, with the centers of the callout and target aligning. + */ + bottomCenter: 5 as 5, + + /** + * Appear below the target element, with the right edges of the callout and target aligning. + */ + bottomRightEdge: 6 as 6, + + /** + * Appear below the target element, aligning with the target element such that the callout tends toward + * the center of the screen. + */ + bottomAutoEdge: 7 as 7, + + /** + * Appear to the left of the target element, with the top edges of the callout and target aligning. + */ + leftTopEdge: 8 as 8, + + /** + * Appear to the left of the target element, with the centers of the callout and target aligning. + */ + leftCenter: 9 as 9, + + /** + * Appear to the left of the target element, with the bottom edges of the callout and target aligning. + */ + leftBottomEdge: 10 as 10, + + /** + * Appear to the right of the target element, with the top edges of the callout and target aligning. + */ + rightTopEdge: 11 as 11, + + /** + * Appear to the right of the target element, with the centers of the callout and target aligning. + */ + rightCenter: 12 as 12, + + /** + * Appear to the right of the target element, with the bottom edges of the callout and target aligning. + */ + rightBottomEdge: 13 as 13, +}; + +export type DirectionalHint = typeof DirectionalHint[keyof typeof DirectionalHint]; diff --git a/packages/react-next/src/common/DocPage.types.ts b/packages/react-next/src/common/DocPage.types.ts new file mode 100644 index 0000000000000..f6aa6b0c0c638 --- /dev/null +++ b/packages/react-next/src/common/DocPage.types.ts @@ -0,0 +1,163 @@ +import { IStyleFunctionOrObject, Omit } from '../Utilities'; +import { ITheme, IStyle } from '../Styling'; + +export interface IExample { + /** Title of the example */ + title: string; + + /** Raw source code of the example */ + code: string; + + /** Working example of the example */ + view: JSX.Element; + + isScrollable?: boolean; + + /** JS String for codepen of the example */ + codepenJS?: string; + + /** Custom styles. Partial version of `IExampleCardProps['styles']`. */ + styles?: IStyleFunctionOrObject<{ theme?: ITheme }, { root: IStyle }>; +} + +export interface IDocPageProps { + /** Title that goes into the header */ + title: string; + + /** Name of the component being documented */ + componentName: string; + + /** URL of the checked in component, should be somewhere on github.com */ + componentUrl: string; + + /** Knobs that applies to all the examples */ + exampleKnobs?: JSX.Element; + + /** Array of examples, displayed in the order defined */ + examples?: IExample[]; + + /** Properties table(s) as markdown string */ + propertiesTablesSources?: string[]; + + /** Overview of the component as markdown string */ + overview?: string; + + /** Accessibility of the component as markdown string */ + accessibility?: string; + + /** DO's blurb as markdown string */ + dos?: string; + + /** DON'Ts blurb as markdown string */ + donts?: string; + + /** Best practice as markdown string */ + bestPractices?: string; + + /** Feedback section includes link to new issue page and displays Github issues */ + isFeedbackVisible?: boolean; + + /** Passed through header visibility flag from the demo component page component */ + isHeaderVisible: boolean; + + /** If true, the component accepts all native props from elements specified in `nativePropsElement` */ + allowNativeProps?: boolean; + + /** Override component name to use in the native props message */ + allowNativePropsForComponentName?: string; + + /** + * Element(s) whose native props this component accepts (default div). + * Only relevant if `allowNativeProps` is true. + */ + nativePropsElement?: string | string[]; + + /** + * Related link + * @deprecated No longer shown on ComponentPage + */ + related?: JSX.Element; + + /** Pass through other sections for ComponentPage */ + otherSections?: { + title: string; + section: JSX.Element; + }[]; + + /** + * JSON to populate the api reference tables + */ + jsonDocs?: IPageJson; +} + +/** + * Used to keep track of where an API reference page will live on the site. + */ +export type PageKind = 'References' | 'Components'; + +/** + * Text excerpt token that is part of a type definition or extends block and may have a link + * to another doc page. For API reference tables. + */ +export interface ILinkToken { + text: string; + /** If this token is a link, name of the doc page it points to */ + linkedPage?: string; + /** If this token is a link, group/category of the doc page it points to */ + linkedPageGroup?: string; +} + +/** + * Generic row for API reference tables. + * It can represent a member (property or method) of an interface or class. + */ +export interface ITableRowJson { + name: string; + kind?: 'method' | 'property'; + /** + * The row's type translated to an array of text elements and links to other types. + * For example, `Readonly` would translate to: + * `[{ text: 'Readonly<' }, { text: 'IFoo', hyperlinkedPage: '(page name)', pageKind: '(kind)' }, { text: '>' }]` + */ + typeTokens: ILinkToken[]; + defaultValue?: string; + description: string; + deprecated: boolean; + deprecatedMessage?: string; + required?: boolean; +} + +/** + * Enum member row for API reference tables. + */ +export type IEnumTableRowJson = Omit & { + value: string; +}; + +export type ApiKind = 'interface' | 'enum' | 'class' | 'typeAlias'; + +/** + * Info for a table representing a top-level API item: interface, enum, class, or type alias. + */ +export interface ITableJson { + kind: ApiKind; + name: string; + /** + * Any types the item extends, translated to an array of text elements and links to other types. + * For classes and interfaces only. + */ + extendsTokens: ILinkToken[]; + description: string; + members?: ITableRowJson[] | IEnumTableRowJson[]; + deprecated?: boolean; + deprecatedMessage?: string; +} + +/** + * Structure of the page.json files + */ +export interface IPageJson { + tables: ITableJson[]; + name: string; + group?: string; +} diff --git a/packages/react-next/src/common/IAccessiblePopupProps.ts b/packages/react-next/src/common/IAccessiblePopupProps.ts new file mode 100644 index 0000000000000..8d010ca9def89 --- /dev/null +++ b/packages/react-next/src/common/IAccessiblePopupProps.ts @@ -0,0 +1,39 @@ +/** + * {@docCategory IAccessiblePopupProps} + */ +export interface IAccessiblePopupProps { + /** + * Sets the element to focus on when exiting the control's FocusTrapZone. + * @defaultvalue The `element.target` that triggered the control opening. + */ + elementToFocusOnDismiss?: HTMLElement; + + /** + * If false (the default), the control's FocusTrapZone will restore focus to the element which + * activated it once the trap zone is unmounted or disabled. Set to true to disable this behavior. + * @defaultvalue false + */ + ignoreExternalFocusing?: boolean; + + /** + * Whether control should force focus inside its focus trap zone. + * @defaultvalue true + */ + forceFocusInsideTrap?: boolean; + + /** + * Class name (not actual selector) for first focusable item. Do not append a dot. + */ + firstFocusableSelector?: string | (() => string); + + /** + * Aria label on close button. + */ + closeButtonAriaLabel?: string; + + /** + * Whether this control will allow clicks outside its FocusTrapZone. + * @defaultvalue false + */ + isClickableOutsideFocusTrap?: boolean; +} diff --git a/packages/react-next/src/common/TestImages.ts b/packages/react-next/src/common/TestImages.ts new file mode 100644 index 0000000000000..b0c19e972367c --- /dev/null +++ b/packages/react-next/src/common/TestImages.ts @@ -0,0 +1,18 @@ +// If this file is moved or split, the scripts for building codepen examples will likely need to be updated. + +const baseProductionCdnUrl = 'https://static2.sharepointonline.com/files/fabric/office-ui-fabric-react-assets/'; + +/** @deprecated Use the version from `@uifabric/example-data` instead. */ +export const TestImages = { + choiceGroupBarUnselected: baseProductionCdnUrl + 'choicegroup-bar-unselected.png', + choiceGroupBarSelected: baseProductionCdnUrl + 'choicegroup-bar-selected.png', + choiceGroupPieUnselected: baseProductionCdnUrl + 'choicegroup-pie-unselected.png', + choiceGroupPieSelected: baseProductionCdnUrl + 'choicegroup-pie-selected.png', + documentPreview: baseProductionCdnUrl + 'document-preview.png', + documentPreviewTwo: baseProductionCdnUrl + 'document-preview2.png', + documentPreviewThree: baseProductionCdnUrl + 'document-preview3.png', + iconOne: baseProductionCdnUrl + 'icon-one.png', + iconPpt: 'https://static2.sharepointonline.com/files/fabric/assets/item-types/32/pptx.png', + personaFemale: baseProductionCdnUrl + 'persona-female.png', + personaMale: baseProductionCdnUrl + 'persona-male.png', +}; diff --git a/packages/react-next/src/common/shallowUntilTarget.ts b/packages/react-next/src/common/shallowUntilTarget.ts new file mode 100644 index 0000000000000..4641925afbf68 --- /dev/null +++ b/packages/react-next/src/common/shallowUntilTarget.ts @@ -0,0 +1,68 @@ +import * as React from 'react'; +import { shallow, ShallowWrapper } from 'enzyme'; + +/** + * Duplicated enzyme's ShallowRendererProps + * + * @internal + */ +export interface IShallowRendererProps { + lifecycleExperimental?: boolean; + disableLifecycleMethods?: boolean; +} + +/** + * ShallowUntilTarget Interface + * + * @internal + */ +export interface IShallowUntilTarget { + maxTries: number; + shallowOptions: IShallowRendererProps; +} + +/** + * An extention of enzyme's shallow function which will fail to work + * with decorated components and/or components using the styled() function. + * This function allows you to pass a 'target' component (e.g. ComponentBase) + * and keep running shallow on each child component till a match is found. + * + * @public + */ +export function shallowUntilTarget( + componentInstance: React.ReactElement

    , + TargetComponent: string, + options: IShallowUntilTarget = { + maxTries: 10, + shallowOptions: {}, + }, +): ShallowWrapper { + const { maxTries, shallowOptions } = options; + + let root = shallow(componentInstance, shallowOptions); + let rootType = root.type(); + + if (typeof rootType === 'string' || rootType.toString().indexOf(TargetComponent) !== -1) { + // Default shallow() + // If type() is a string then it's a DOM Node. + // If it were wrapped, it would be a React component. + return root; + } + + for (let tries = 1; tries <= maxTries; tries++) { + // Check for target as a string to avoid conflicts + // with decoratored components name + if (rootType.toString().indexOf(TargetComponent) !== -1) { + // Now that we found the target component, render it. + return root.first().shallow(shallowOptions); + } + // Unwrap the next component in the hierarchy. + root = root.first().shallow(shallowOptions); + rootType = root.type(); + } + + throw new Error( + `Could not find ${TargetComponent} in React instance: ${componentInstance}; + gave up after ${maxTries} tries`, + ); +} diff --git a/packages/react-next/src/common/sharedIsConformant.ts b/packages/react-next/src/common/sharedIsConformant.ts new file mode 100644 index 0000000000000..87164d38f24e9 --- /dev/null +++ b/packages/react-next/src/common/sharedIsConformant.ts @@ -0,0 +1,10 @@ +// eslint-disable-next-line import/no-extraneous-dependencies -- this file is for testing +import { isConformant, IsConformantOptions } from '@fluentui/react-conformance'; + +export function sharedIsConformant(testInfo: IsConformantOptions) { + const defaultOptions = { + disabledTests: ['has-docblock'], + }; + + isConformant(defaultOptions, testInfo); +} diff --git a/packages/react-next/src/common/testUtilities.ts b/packages/react-next/src/common/testUtilities.ts new file mode 100644 index 0000000000000..81de215e56d85 --- /dev/null +++ b/packages/react-next/src/common/testUtilities.ts @@ -0,0 +1,63 @@ +import * as React from 'react'; +import { ReactWrapper, mount } from 'enzyme'; +import { Component } from 'react'; +import * as ReactDOM from 'react-dom'; +import * as ReactTestUtils from 'react-dom/test-utils'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function findNodes(wrapper: ReactWrapper, className: string): ReactWrapper { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return wrapper.find(className).filterWhere((node: ReactWrapper) => typeof node.type() === 'string'); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function expectNodes(wrapper: ReactWrapper, className: string, n: number): void { + expect(findNodes(wrapper, className).length).toEqual(n); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function expectOne(wrapper: ReactWrapper, className: string): void { + expectNodes(wrapper, className, 1); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function expectMissing(wrapper: ReactWrapper, className: string): void { + expectNodes(wrapper, className, 0); +} + +/** @deprecated Use fake timers and `jest.runAllTimers()` instead */ +export function delay(millisecond: number): Promise { + return new Promise(resolve => setTimeout(resolve, millisecond)); +} + +/** + * Mounts the element attached to a child of document.body. This is primarily for tests involving + * event handlers (which don't work right unless the element is attached). + */ +export function mountAttached( + element: React.ReactElement

    , +): ReactWrapper { + const parent = document.createElement('div'); + document.body.appendChild(parent); + return mount(element, { attachTo: parent }); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function renderIntoDocument(element: React.ReactElement): HTMLElement { + const component = ReactTestUtils.renderIntoDocument(element); + const renderedDOM = ReactDOM.findDOMNode(component as React.ReactInstance); + return renderedDOM as HTMLElement; +} + +export function mockEvent(targetValue: string = ''): ReactTestUtils.SyntheticEventData { + const target: EventTarget = { value: targetValue } as HTMLInputElement; + return { target }; +} + +/** + * Hack for forcing Jest to run pending promises + * https://github.com/facebook/jest/issues/2157#issuecomment-279171856 + */ +export function flushPromises() { + return new Promise(resolve => setImmediate(resolve)); +} From 39d8a279a7f09b82acb7ad7e6b6b797546c1cdd3 Mon Sep 17 00:00:00 2001 From: Michael Loughry Date: Tue, 11 Aug 2020 19:35:50 +0000 Subject: [PATCH 8/9] Revert changes to common --- packages/react-next/src/common/sharedIsConformant.ts | 2 +- packages/react-next/src/common/testUtilities.ts | 7 ++----- packages/react-next/src/components/Slider/Slider.test.tsx | 8 ++++++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/react-next/src/common/sharedIsConformant.ts b/packages/react-next/src/common/sharedIsConformant.ts index 87164d38f24e9..203956af81a69 100644 --- a/packages/react-next/src/common/sharedIsConformant.ts +++ b/packages/react-next/src/common/sharedIsConformant.ts @@ -3,7 +3,7 @@ import { isConformant, IsConformantOptions } from '@fluentui/react-conformance'; export function sharedIsConformant(testInfo: IsConformantOptions) { const defaultOptions = { - disabledTests: ['has-docblock'], + disabledTests: ['has-docblock', 'kebab-aria-attributes'], }; isConformant(defaultOptions, testInfo); diff --git a/packages/react-next/src/common/testUtilities.ts b/packages/react-next/src/common/testUtilities.ts index 81de215e56d85..c80b031a6f429 100644 --- a/packages/react-next/src/common/testUtilities.ts +++ b/packages/react-next/src/common/testUtilities.ts @@ -4,26 +4,23 @@ import { Component } from 'react'; import * as ReactDOM from 'react-dom'; import * as ReactTestUtils from 'react-dom/test-utils'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any +/* eslint-disable @typescript-eslint/no-explicit-any */ export function findNodes(wrapper: ReactWrapper, className: string): ReactWrapper { - // eslint-disable-next-line @typescript-eslint/no-explicit-any return wrapper.find(className).filterWhere((node: ReactWrapper) => typeof node.type() === 'string'); } -// eslint-disable-next-line @typescript-eslint/no-explicit-any export function expectNodes(wrapper: ReactWrapper, className: string, n: number): void { expect(findNodes(wrapper, className).length).toEqual(n); } -// eslint-disable-next-line @typescript-eslint/no-explicit-any export function expectOne(wrapper: ReactWrapper, className: string): void { expectNodes(wrapper, className, 1); } -// eslint-disable-next-line @typescript-eslint/no-explicit-any export function expectMissing(wrapper: ReactWrapper, className: string): void { expectNodes(wrapper, className, 0); } +/* eslint-enable @typescript-eslint/no-explicit-any */ /** @deprecated Use fake timers and `jest.runAllTimers()` instead */ export function delay(millisecond: number): Promise { diff --git a/packages/react-next/src/components/Slider/Slider.test.tsx b/packages/react-next/src/components/Slider/Slider.test.tsx index 2400a08f40dac..64eb662bd17b8 100644 --- a/packages/react-next/src/components/Slider/Slider.test.tsx +++ b/packages/react-next/src/components/Slider/Slider.test.tsx @@ -2,11 +2,13 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { create } from '@uifabric/utilities/lib/test'; import * as ReactTestUtils from 'react-dom/test-utils'; +import * as path from 'path'; import { mount, ReactWrapper } from 'enzyme'; import { Slider } from './Slider'; import { ISlider } from './Slider.types'; import { ONKEYDOWN_TIMEOUT_DURATION } from './Slider.base'; +import { sharedIsConformant } from '../../common/sharedIsConformant'; import { resetIds, KeyCodes } from '@uifabric/utilities'; describe('Slider', () => { @@ -23,6 +25,12 @@ describe('Slider', () => { } }); + sharedIsConformant({ + componentPath: path.join(__dirname, 'Slider.tsx'), + Component: Slider, + displayName: 'Slider', + }); + it('renders correctly', () => { const component = create(); const tree = component.toJSON(); From 211263f238d1cc6a36839d459441315a2a6f76ed Mon Sep 17 00:00:00 2001 From: Michael Loughry Date: Tue, 11 Aug 2020 16:20:35 -0700 Subject: [PATCH 9/9] Update packages/react-next/src/components/ContextualMenu/ContextualMenu.classNames.ts Co-authored-by: Elizabeth Craig --- .../components/ContextualMenu/ContextualMenu.classNames.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-next/src/components/ContextualMenu/ContextualMenu.classNames.ts b/packages/react-next/src/components/ContextualMenu/ContextualMenu.classNames.ts index 134a7f9d51fdd..dcdd6cc3ff03b 100644 --- a/packages/react-next/src/components/ContextualMenu/ContextualMenu.classNames.ts +++ b/packages/react-next/src/components/ContextualMenu/ContextualMenu.classNames.ts @@ -1,7 +1,7 @@ -import { getDividerClassNames } from 'office-ui-fabric-react/src/components/Divider/VerticalDivider.classNames'; +import { getDividerClassNames } from 'office-ui-fabric-react/lib/components/Divider/VerticalDivider.classNames'; import { getMenuItemStyles } from './ContextualMenu.cnstyles'; import { ITheme, mergeStyleSets, getGlobalClassNames, getScreenSelector, ScreenWidthMaxMedium } from '../../Styling'; -import { IVerticalDividerClassNames } from 'office-ui-fabric-react/src/components/Divider/VerticalDivider.types'; +import { IVerticalDividerClassNames } from 'office-ui-fabric-react/lib/components/Divider/VerticalDivider.types'; import { memoizeFunction, IsFocusVisibleClassName } from '../../Utilities'; import { IContextualMenuItemStyles, IContextualMenuItemStyleProps } from './ContextualMenuItem.types'; import { IContextualMenuSubComponentStyles } from './ContextualMenu.types';