Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Allow `useRef` hook used for storing debugging data to be defined in any order with other hooks in functional components @layershifter, @mnajdova ([#2236](https://github.com/microsoft/fluent-ui-react/pull/2236))
- Add `useStyles()` hook to use theming capabilities in custom components @layershifter, @mnajdova ([#2217](https://github.com/microsoft/fluent-ui-react/pull/2217))
- Add optional wrapper function to `List` which can be used to inject custom scrollbars to `Dropdown` @jurokapsiar ([#2092](https://github.com/microsoft/fluent-ui-react/pull/2092))
- Add `useTelemetry()` hook for adding telemetry information for the Fluent components and improve return types for the `useStyles` and `useStateManager` hooks @mnajdova ([#2257](https://github.com/microsoft/fluent-ui-react/pull/2257))

### Documentation
- Add per-component performance charts @miroslavstastny ([#2240](https://github.com/microsoft/fluent-ui-react/pull/2240))
Expand Down
3 changes: 2 additions & 1 deletion docs/src/prototypes/customToolbar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import * as _ from 'lodash'
import * as React from 'react'
import { KnobsSnippet } from '@fluentui/code-sandbox'
import { Telemetry } from '@fluentui/react-bindings'
import {
KnobProvider,
useBooleanKnob,
useSelectKnob,
KnobInspector,
} from '@fluentui/docs-components'
import { Provider, Flex, themes, mergeThemes, Telemetry } from '@fluentui/react'
import { Provider, Flex, themes, mergeThemes } from '@fluentui/react'

import { darkThemeOverrides } from './darkThemeOverrides'
import { highContrastThemeOverrides } from './highContrastThemeOverrides'
Expand Down
10 changes: 5 additions & 5 deletions packages/react-bindings/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ const createInputManager: ManagerFactory<InputState, InputActions> = config =>
})

const Input: React.FC<InputProps> = props => {
const [state, actions] = useStateManager(createInputManager, {
const { state, actions } = useStateManager(createInputManager, {
mapPropsToInitialState: () => ({ value: props.defaultValue }),
mapPropsToState: () => ({ value: props.value }),
})
Expand All @@ -136,8 +136,8 @@ const Input: React.FC<InputProps> = props => {
### Reference

```tsx
const [state, actions] = useStateManager(createInputManager)
const [state, actions] = useStateManager(
const { state, actions } = useStateManager(createInputManager)
const { state, actions } = useStateManager(
managerFactory: ManagerFactory<State, Actions>,
options: UseStateManagerOptions<Props>,
)
Expand All @@ -164,7 +164,7 @@ type TextComponentProps = {
const Text: React.FunctionComponent<TextComponentProps> = props => {
const { className, children, color } = props

const [classes] = useStyles('Text', {
const { classes } = useStyles('Text', {
className: 'ui-text',
mapPropsToStyles: () => ({ color }),
})
Expand All @@ -176,7 +176,7 @@ const Text: React.FunctionComponent<TextComponentProps> = props => {
### Reference

```tsx
const [classes] = useStyles(
const { classes } = useStyles(
displayName: string,
options: UseStylesOptions<Props>,
)
Expand Down
14 changes: 11 additions & 3 deletions packages/react-bindings/src/hooks/useStateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ type UseStateManagerOptions<State> = {
sideEffects?: SideEffect<State>[]
}

type UseStateManagerResult<State, Actions> = {
state: Readonly<State>
actions: Readonly<Actions>
}

const getDefinedProps = <Props extends Record<string, any>>(props: Props): Partial<Props> => {
const definedProps: Partial<Props> = {}

Expand All @@ -25,7 +30,7 @@ const useStateManager = <
>(
managerFactory: ManagerFactory<State, Actions>,
options: UseStateManagerOptions<State> = {},
): [Readonly<State>, Readonly<Actions>] => {
): UseStateManagerResult<State, Actions> => {
const {
mapPropsToInitialState = () => ({} as Partial<State>),
mapPropsToState = () => ({} as Partial<State>),
Expand Down Expand Up @@ -58,11 +63,14 @@ const useStateManager = <
// https://github.com/facebook/react/issues/11527#issuecomment-360199710

if (process.env.NODE_ENV === 'production') {
return [latestManager.current.state, latestManager.current.actions]
return { state: latestManager.current.state, actions: latestManager.current.actions }
}

// Object.freeze() is used only in dev-mode to avoid usage mistakes
return [Object.freeze(latestManager.current.state), Object.freeze(latestManager.current.actions)]
return {
state: Object.freeze(latestManager.current.state),
actions: Object.freeze(latestManager.current.actions),
}
}

export default useStateManager
9 changes: 7 additions & 2 deletions packages/react-bindings/src/hooks/useStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ type UseStylesOptions<StyleProps extends PrimitiveProps> = {
rtl?: boolean
}

type UseStylesResult = {
classes: ComponentSlotClasses
styles: ComponentSlotStylesPrepared
}

type InlineStyleProps<StyleProps> = {
/** Additional CSS class name(s) to apply. */
className?: string
Expand All @@ -48,7 +53,7 @@ const defaultContext: StylesContextValue<{ renderRule: RendererRenderRule }> = {
const useStyles = <StyleProps extends PrimitiveProps>(
displayName: string,
options: UseStylesOptions<StyleProps>,
): [ComponentSlotClasses, ComponentSlotStylesPrepared] => {
): UseStylesResult => {
const context: StylesContextValue<{ renderRule: RendererRenderRule }> =
React.useContext(ThemeContext) || defaultContext

Expand Down Expand Up @@ -79,7 +84,7 @@ const useStyles = <StyleProps extends PrimitiveProps>(
_internal_resolvedComponentVariables: context._internal_resolvedComponentVariables,
})

return [classes, resolvedStyles]
return { classes, styles: resolvedStyles }
}

export default useStyles
3 changes: 3 additions & 0 deletions packages/react-bindings/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ export { default as unstable_createAnimationStyles } from './styles/createAnimat
export { default as unstable_getStyles } from './styles/getStyles'
export * from './styles/types'

export { default as useTelemetry } from './telemetry/useTelemetry'
export * from './telemetry/types'

export { default as getElementType } from './utils/getElementType'
export { default as getUnhandledProps } from './utils/getUnhandledProps'
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ type ComponentPerfStats = {
msMax: number
}

export default class Telemetry {
export type UseTelemetryResult = {
setStart: () => void
setEnd: () => void
}

export class Telemetry {
performance: Record<string, ComponentPerfStats>
enabled: boolean

Expand Down
43 changes: 43 additions & 0 deletions packages/react-bindings/src/telemetry/useTelemetry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Telemetry, UseTelemetryResult } from './types'

const useTelemetry = (
displayName: string,
telemetry: Telemetry | undefined,
): UseTelemetryResult => {
let start: number = -1
let end: number = -1

const setStart = () => {
start = telemetry && telemetry.enabled ? performance.now() : -1
}

const setEnd = () => {
if (telemetry && telemetry.enabled && start !== -1) {
end = performance.now()
const duration = end - start
if (telemetry.performance[displayName]) {
telemetry.performance[displayName].count++
telemetry.performance[displayName].msTotal += duration
telemetry.performance[displayName].msMin = Math.min(
duration,
telemetry.performance[displayName].msMin,
)
telemetry.performance[displayName].msMax = Math.max(
duration,
telemetry.performance[displayName].msMax,
)
} else {
telemetry.performance[displayName] = {
count: 1,
msTotal: duration,
msMin: duration,
msMax: duration,
}
}
}
}

return { setStart, setEnd }
}

export default useTelemetry
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const TestComponent: React.FunctionComponent<TestComponentProps> = props => {
}
},
)
const [state, actions] = useStateManager(createTestManager, {
const { state, actions } = useStateManager(createTestManager, {
mapPropsToInitialState: () => ({ value: props.defaultValue }),
mapPropsToState: () => ({ value: props.value }),
sideEffects: [dispatchEffect],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type TestComponentProps = Partial<TestState> & {
}

const TestComponent: React.FunctionComponent<TestComponentProps> = props => {
const [state, actions] = useStateManager(createTestManager, {
const { state, actions } = useStateManager(createTestManager, {
mapPropsToInitialState: () => ({
open: props.defaultOpen,
value: props.defaultValue,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-bindings/test/hooks/useStyles-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type TestComponentProps = {
const TestComponent: React.FunctionComponent<TestComponentProps> = props => {
const { className, color, styles, variables } = props

const [classes] = useStyles('Test', {
const { classes } = useStyles('Test', {
className: 'ui-test',
mapPropsToStyles: () => ({ color }),
mapPropsToInlineStyles: () => ({ className, styles, variables }),
Expand Down
1 change: 0 additions & 1 deletion packages/react/src/components/Animation/Animation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ class Animation extends UIComponent<WithAsProp<AnimationProps>, any> {
static propTypes = {
...commonPropTypes.createCommon({
accessibility: false,
animated: false,
content: false,
children: 'element',
}),
Expand Down
1 change: 0 additions & 1 deletion packages/react/src/components/MenuButton/MenuButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ export default class MenuButton extends AutoControlledComponent<MenuButtonProps,

static propTypes = {
...commonPropTypes.createCommon({
animated: false,
as: true,
content: false,
}),
Expand Down
1 change: 0 additions & 1 deletion packages/react/src/components/Popup/Popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ export default class Popup extends AutoControlledComponent<PopupProps, PopupStat

static propTypes = {
...commonPropTypes.createCommon({
animated: false,
as: false,
content: false,
}),
Expand Down
1 change: 0 additions & 1 deletion packages/react/src/components/Portal/Portal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ class Portal extends AutoControlledComponent<PortalProps, PortalState> {
static propTypes = {
...commonPropTypes.createCommon({
accessibility: false,
animated: false,
as: false,
className: false,
styled: false,
Expand Down
1 change: 0 additions & 1 deletion packages/react/src/components/Portal/PortalInner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ class PortalInner extends React.Component<PortalInnerProps> {
static propTypes = {
...commonPropTypes.createCommon({
accessibility: false,
animated: false,
as: false,
className: false,
content: false,
Expand Down
3 changes: 1 addition & 2 deletions packages/react/src/components/Provider/Provider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IStyle } from 'fela'
import * as _ from 'lodash'
import { Renderer } from '@fluentui/react-bindings'
import { Renderer, Telemetry } from '@fluentui/react-bindings'
import * as customPropTypes from '@fluentui/react-proptypes'
import {
mergeSiteVariables,
Expand Down Expand Up @@ -28,7 +28,6 @@ import {
withSafeTypeForAs,
} from '../../types'
import mergeContexts from '../../utils/mergeProviderContexts'
import Telemetry from '../../utils/Telemetry'

export interface ProviderProps extends ChildrenComponentProps {
renderer?: Renderer
Expand Down
1 change: 0 additions & 1 deletion packages/react/src/components/Tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ export default class Tooltip extends AutoControlledComponent<TooltipProps, Toolt

static propTypes = {
...commonPropTypes.createCommon({
animated: false,
as: false,
content: false,
}),
Expand Down
4 changes: 1 addition & 3 deletions packages/react/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { StylesContextInputValue, StylesContextValue } from '@fluentui/react-bindings'
import { StylesContextInputValue, StylesContextValue, Telemetry } from '@fluentui/react-bindings'
import * as React from 'react'

import Telemetry from './utils/Telemetry'

// ========================================================
// Utilities
// ========================================================
Expand Down
5 changes: 0 additions & 5 deletions packages/react/src/utils/commonPropTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import * as PropTypes from 'prop-types'

export interface CreateCommonConfig {
accessibility?: boolean
animated?: boolean
children?: boolean | 'node' | 'element'
as?: boolean
className?: boolean
Expand All @@ -15,7 +14,6 @@ export interface CreateCommonConfig {
export const createCommon = (config: CreateCommonConfig = {}) => {
const {
accessibility = true,
animated = true,
as = true,
children = 'node',
className = true,
Expand All @@ -27,9 +25,6 @@ export const createCommon = (config: CreateCommonConfig = {}) => {
...(accessibility && {
accessibility: customPropTypes.accessibility,
}),
...(animated && {
animation: customPropTypes.animation,
}),
...(as && {
as: PropTypes.elementType,
}),
Expand Down
1 change: 0 additions & 1 deletion packages/react/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,3 @@ export module commonPropTypes {
export type CreateCommonConfig = CreateCommonConfigLocal
export const createCommon = createCommonLocal
}
export { default as Telemetry } from './Telemetry'
30 changes: 4 additions & 26 deletions packages/react/src/utils/renderComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
ReactAccessibilityBehavior,
unstable_getAccessibility as getAccessibility,
unstable_getStyles as getStyles,
useTelemetry,
} from '@fluentui/react-bindings'
import {
emptyTheme,
Expand All @@ -24,7 +25,6 @@ import * as React from 'react'

import { Props, ProviderContextPrepared } from '../types'
import logProviderMissingWarning from './providerMissingHandler'
import Telemetry from './Telemetry'

export interface RenderResultConfig<P> {
ElementType: React.ElementType<P>
Expand Down Expand Up @@ -69,10 +69,10 @@ const renderComponent = <P extends {}>(
logProviderMissingWarning()
}

const { telemetry = undefined as Telemetry } = context || {}
const { setStart, setEnd } = useTelemetry(displayName, context.telemetry)
const rtl = context.rtl || false

const startTime = telemetry && telemetry.enabled ? performance.now() : 0
setStart()

const ElementType = getElementType(props) as React.ReactType<P>
const unhandledProps = getUnhandledProps(handledProps, props)
Expand Down Expand Up @@ -109,29 +109,7 @@ const renderComponent = <P extends {}>(
}
let wrapInFocusZone: (element: React.ReactElement) => React.ReactElement = element => element

if (telemetry && telemetry.enabled) {
const duration = performance.now() - startTime

if (telemetry.performance[displayName]) {
telemetry.performance[displayName].count++
telemetry.performance[displayName].msTotal += duration
telemetry.performance[displayName].msMin = Math.min(
duration,
telemetry.performance[displayName].msMin,
)
telemetry.performance[displayName].msMax = Math.max(
duration,
telemetry.performance[displayName].msMax,
)
} else {
telemetry.performance[displayName] = {
count: 1,
msTotal: duration,
msMin: duration,
msMax: duration,
}
}
}
setEnd()

if (accessibility.focusZone && accessibility.focusZone.mode === FocusZoneMode.Wrap) {
wrapInFocusZone = element =>
Expand Down