diff --git a/change/@fluentui-react-button-2020-06-19-15-32-16-fix-button-fixes.json b/change/@fluentui-react-button-2020-06-19-15-32-16-fix-button-fixes.json new file mode 100644 index 0000000000000..51c143149ed2c --- /dev/null +++ b/change/@fluentui-react-button-2020-06-19-15-32-16-fix-button-fixes.json @@ -0,0 +1,8 @@ +{ + "type": "minor", + "comment": "Button: Adding componentRef, focus method, styling, and tests.", + "packageName": "@fluentui/react-button", + "email": "dzearing@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-06-19T22:32:16.380Z" +} diff --git a/packages/react-button/etc/react-button.api.md b/packages/react-button/etc/react-button.api.md index 5a6621c125702..a4d939ffa7500 100644 --- a/packages/react-button/etc/react-button.api.md +++ b/packages/react-button/etc/react-button.api.md @@ -1,139 +1,147 @@ -## API Report File for "@fluentui/react-button" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import { BaseSlots } from '@fluentui/react-compose'; -import { ColorPlateSet } from '@fluentui/react-theme-provider'; -import { ComposeOptions } from '@fluentui/react-compose'; -import { ComposePreparedOptions } from '@fluentui/react-compose'; -import * as React from 'react'; -import { SlotProps } from '@fluentui/react-compose'; - -// @public (undocumented) -export const Button: import("@fluentui/react-compose").ComponentWithAs<"button", ButtonProps>; - -// @public (undocumented) -export const ButtonBase: import("@fluentui/react-compose").ComponentWithAs<"button", ButtonProps>; - -// Warning: (ae-forgotten-export) The symbol "ComposeStandardStatics" needs to be exported by the entry point index.d.ts -// -// @public (undocumented) -export interface ButtonOptions extends ComposeOptions { -} - -// Warning: (ae-forgotten-export) The symbol "ComponentProps" needs to be exported by the entry point index.d.ts -// -// @public (undocumented) -export interface ButtonProps extends ComponentProps, React.HTMLAttributes { - circular?: boolean; - content?: ShorthandValue<{}>; - disabled?: boolean; - fluid?: boolean; - // Warning: (ae-forgotten-export) The symbol "ShorthandValue" needs to be exported by the entry point index.d.ts - icon?: ShorthandValue<{}>; - iconOnly?: boolean; - iconPosition?: 'before' | 'after'; - inverted?: boolean; - loader?: ShorthandValue<{}>; - loading?: boolean; - primary?: boolean; - secondary?: boolean; - size?: SizeValue; - // Warning: (ae-forgotten-export) The symbol "RecursivePartial" needs to be exported by the entry point index.d.ts - tokens?: RecursivePartial; -} - -// @public (undocumented) -export type ButtonSlotProps = SlotProps>; - -// @public (undocumented) -export interface ButtonSlots extends BaseSlots { - // (undocumented) - content: React.ElementType; - // (undocumented) - icon: React.ElementType; - // (undocumented) - loader: React.ElementType; -} - -// @public (undocumented) -export interface ButtonState extends ButtonProps { -} - -// @public (undocumented) -export type ButtonTokens = ColorPlateSet & { - padding: string; - margin: string; - height: string; - minWidth: string; - maxWidth: string; - contentGap: string; - iconSize: string; - borderRadius: string; - borderWidth: string; - boxShadow: string; - width: string; - size: { - smallest: string; - smaller: string; - small: string; - regular: string; - large: string; - larger: string; - largest: string; - }; - transform: string; - transition: string; - fontFamily: string; - fontSize: string; - fontWeight: string; - pressed: { - transform: string; - }; -}; - -// @public (undocumented) -export type SizeValue = 'smallest' | 'smaller' | 'small' | 'medium' | 'large' | 'larger' | 'largest'; - -// @public (undocumented) -export const ToggleButton: import("@fluentui/react-compose").ComponentWithAs<"button", ToggleButtonProps>; - -// @public (undocumented) -export const ToggleButtonBase: import("@fluentui/react-compose").ComponentWithAs<"button", ToggleButtonProps & ButtonProps>; - -// @public (undocumented) -export interface ToggleButtonOptions extends ComposeOptions { -} - -// @public (undocumented) -export interface ToggleButtonProps extends ButtonProps { - checked?: boolean; - defaultChecked?: boolean; -} - -// @public (undocumented) -export type ToggleButtonSlotProps = SlotProps>; - -// @public (undocumented) -export interface ToggleButtonSlots extends ButtonSlots { -} - -// @public (undocumented) -export interface ToggleButtonState extends ToggleButtonProps { -} - -// @public -export const useButton: (props: ButtonProps, ref: import("react").Ref, options: ComposePreparedOptions<{}, any, {}>) => ButtonState; - -// Warning: (ae-forgotten-export) The symbol "ToggleProps" needs to be exported by the entry point index.d.ts -// Warning: (ae-forgotten-export) The symbol "ToggleState" needs to be exported by the entry point index.d.ts -// -// @public -export const useToggle: (props: TProps & ToggleProps) => TState & ToggleState; - - -// (No @packageDocumentation comment for this package) - -``` +## API Report File for "@fluentui/react-button" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { BaseSlots } from '@fluentui/react-compose'; +import { ColorPlateSet } from '@fluentui/react-theme-provider'; +import { ComposeOptions } from '@fluentui/react-compose'; +import { ComposePreparedOptions } from '@fluentui/react-compose'; +import * as React from 'react'; +import { SlotProps } from '@fluentui/react-compose'; + +// @public (undocumented) +export const Button: import("@fluentui/react-compose").ComponentWithAs<"button", ButtonProps>; + +// @public (undocumented) +export const ButtonBase: import("@fluentui/react-compose").ComponentWithAs<"button", ButtonProps>; + +// Warning: (ae-forgotten-export) The symbol "ComposeStandardStatics" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export interface ButtonOptions extends ComposeOptions { +} + +// Warning: (ae-forgotten-export) The symbol "ComponentProps" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export interface ButtonProps extends ComponentProps, React.HTMLAttributes { + circular?: boolean; + componentRef?: React.RefObject; + content?: ShorthandValue<{}>; + disabled?: boolean; + fluid?: boolean; + // Warning: (ae-forgotten-export) The symbol "ShorthandValue" needs to be exported by the entry point index.d.ts + icon?: ShorthandValue<{}>; + iconOnly?: boolean; + iconPosition?: 'before' | 'after'; + inverted?: boolean; + loader?: ShorthandValue<{}>; + loading?: boolean; + primary?: boolean; + secondary?: boolean; + size?: SizeValue; + // Warning: (ae-forgotten-export) The symbol "RecursivePartial" needs to be exported by the entry point index.d.ts + tokens?: RecursivePartial; +} + +// @public (undocumented) +export interface ButtonRef { + focus: () => void; +} + +// @public (undocumented) +export type ButtonSlotProps = SlotProps>; + +// @public (undocumented) +export interface ButtonSlots extends BaseSlots { + // (undocumented) + content: React.ElementType; + // (undocumented) + icon: React.ElementType; + // (undocumented) + loader: React.ElementType; +} + +// @public (undocumented) +export interface ButtonState extends ButtonProps { + // (undocumented) + buttonRef?: React.RefObject; +} + +// @public (undocumented) +export type ButtonTokens = ColorPlateSet & { + padding: string; + margin: string; + height: string; + minWidth: string; + maxWidth: string; + contentGap: string; + iconSize: string; + borderRadius: string; + borderWidth: string; + boxShadow: string; + width: string; + size: { + smallest: string; + smaller: string; + small: string; + regular: string; + large: string; + larger: string; + largest: string; + }; + transform: string; + transition: string; + fontFamily: string; + fontSize: string; + fontWeight: string; + pressed: { + transform: string; + }; +}; + +// @public (undocumented) +export type SizeValue = 'smallest' | 'smaller' | 'small' | 'medium' | 'large' | 'larger' | 'largest'; + +// @public (undocumented) +export const ToggleButton: import("@fluentui/react-compose").ComponentWithAs<"button", ToggleButtonProps>; + +// @public (undocumented) +export const ToggleButtonBase: import("@fluentui/react-compose").ComponentWithAs<"button", ToggleButtonProps & ButtonProps>; + +// @public (undocumented) +export interface ToggleButtonOptions extends ComposeOptions { +} + +// @public (undocumented) +export interface ToggleButtonProps extends ButtonProps { + checked?: boolean; + defaultChecked?: boolean; +} + +// @public (undocumented) +export type ToggleButtonSlotProps = SlotProps>; + +// @public (undocumented) +export interface ToggleButtonSlots extends ButtonSlots { +} + +// @public (undocumented) +export interface ToggleButtonState extends ToggleButtonProps { +} + +// @public +export const useButton: (props: ButtonProps, ref: import("react").Ref, options: ComposePreparedOptions<{}, any, {}>) => ButtonState; + +// Warning: (ae-forgotten-export) The symbol "ToggleProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "ToggleState" needs to be exported by the entry point index.d.ts +// +// @public +export const useToggle: (props: TProps & ToggleProps) => TState & ToggleState; + + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/react-button/package.json b/packages/react-button/package.json index 351a68f29318e..907be2f1b5af4 100644 --- a/packages/react-button/package.json +++ b/packages/react-button/package.json @@ -38,6 +38,7 @@ "@uifabric/tslint-rules": "^7.2.2", "enzyme": "~3.10.0", "enzyme-adapter-react-16": "^1.15.0", + "office-ui-fabric-react": "^7.121.5", "react": "16.8.6", "react-app-polyfill": "~1.0.1", "react-dom": "16.8.6", @@ -51,7 +52,6 @@ "@uifabric/react-hooks": "^7.4.10", "@uifabric/set-version": "^7.0.15", "@uifabric/utilities": "^7.21.4", - "office-ui-fabric-react": "^7.121.5", "tslib": "^1.10.0" }, "peerDependencies": { diff --git a/packages/react-button/src/components/Button/Button.stories.scss b/packages/react-button/src/components/Button/Button.stories.scss index eced278ce8908..a0d35136a7dfa 100644 --- a/packages/react-button/src/components/Button/Button.stories.scss +++ b/packages/react-button/src/components/Button/Button.stories.scss @@ -14,6 +14,15 @@ flex-direction: column; align-items: flex-start; > *:not(:first-child) { - margin-top: var(--gap, 8px); + margin-top: var(--gap, 20px); } } + +.text { + font-family: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, + 'Helvetica Neue', sans-serif; + font-size: 20px; + font-weight: 600; + margin: 0; + padding: 0; +} diff --git a/packages/react-button/src/components/Button/Button.stories.tsx b/packages/react-button/src/components/Button/Button.stories.tsx index 8be213c952be8..a7c7284b76697 100644 --- a/packages/react-button/src/components/Button/Button.stories.tsx +++ b/packages/react-button/src/components/Button/Button.stories.tsx @@ -2,64 +2,77 @@ import * as React from 'react'; import { Button } from './Button'; import { ButtonProps } from './Button.types'; import * as classes from './Button.stories.scss'; -import { Stack, Text } from 'office-ui-fabric-react'; + +/** + * Temporary Stack until there's one in its own package. + */ +const Stack = (props: React.PropsWithChildren<{ horizontal?: boolean }>) => { + const { horizontal, ...rest } = props; + + return
; +}; + +/** + * Temporary Text until there's one in its own package. + */ +const Text = (props: React.PropsWithChildren<{}>) =>

; const ButtonVariants = (props: ButtonProps) => ( -
-
+ +

+ - A button can contain only an icon using the `iconOnly` prop. + A button can contain only an icon using the `iconOnly` prop. - A button can be both `circular` and `iconOnly`. + A button can be both `circular` and `iconOnly`. - An icon button can format its Icon to appear before or after its content. -
+ An icon button can format its Icon to appear before or after its content. + -
+ - A button can show a loading indicator using the `loading` prop. + A button can show a loading indicator using the `loading` prop. - A button can be sized. -
+ A button can be sized. + -
+ ); export const ButtonTokens = () => ( - - A button can be colored using inline tokens. -
+ + A button can be colored using inline tokens. +
- A tokenized button can be customized for any size or padding. -
- + A tokenized button can be customized for any size or padding. + +
+
); diff --git a/packages/react-button/src/components/Button/Button.test.tsx b/packages/react-button/src/components/Button/Button.test.tsx index 8177bc8d135eb..85764493f5ccb 100644 --- a/packages/react-button/src/components/Button/Button.test.tsx +++ b/packages/react-button/src/components/Button/Button.test.tsx @@ -1,12 +1,48 @@ +import * as React from 'react'; import * as path from 'path'; import { isConformant } from '@fluentui/react-conformance'; import { Button } from './Button'; +import * as renderer from 'react-test-renderer'; +import { ButtonRef } from './Button.types'; +import { mount, ReactWrapper } from 'enzyme'; describe('Button', () => { + let wrapper: ReactWrapper | undefined; + + afterEach(() => { + if (wrapper) { + wrapper.unmount(); + wrapper = undefined; + } + }); + isConformant({ componentPath: path.join(__dirname, 'Button.tsx'), Component: Button, displayName: 'Button', disabledTests: ['has-docblock'], }); + + /** + * Note: see more visual regression tests for Button in /apps/vr-tests. + */ + it('renders a default state', () => { + const component = renderer.create( +`; diff --git a/packages/react-button/src/components/Button/useButton.ts b/packages/react-button/src/components/Button/useButton.ts index 262c07936abd5..2242b03196e4e 100644 --- a/packages/react-button/src/components/Button/useButton.ts +++ b/packages/react-button/src/components/Button/useButton.ts @@ -1,6 +1,7 @@ -import { ComposePreparedOptions } from '@fluentui/react-compose'; +import { useImperativeHandle, useRef } from 'react'; import { getStyleFromPropsAndOptions } from '@fluentui/react-theme-provider'; -import { useFocusRects } from 'office-ui-fabric-react'; +import { useFocusRects } from '@uifabric/utilities'; +import { ComposePreparedOptions } from '@fluentui/react-compose'; import { ButtonProps, ButtonState } from './Button.types'; /** @@ -12,10 +13,16 @@ export const useButton = ( ref: React.Ref, options: ComposePreparedOptions, ): ButtonState => { + const buttonRef = useRef(null); + + useImperativeHandle(props.componentRef, () => ({ + focus: () => buttonRef.current?.focus(), + })); useFocusRects(ref as React.RefObject); return { ...props, + buttonRef, style: getStyleFromPropsAndOptions(props, options, '--button'), }; };