Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Button: Adding various tests and updating component to include componentRef #13718

Merged
merged 24 commits into from
Jun 30, 2020
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e46ec38
Updates.
dzearing Jun 19, 2020
cac042b
useMergedRefs update to be immutable.
dzearing Jun 19, 2020
5a7bb14
adding comment.
dzearing Jun 19, 2020
b76e986
Change files
dzearing Jun 19, 2020
b035448
more updates.
dzearing Jun 19, 2020
c9a2778
updates
dzearing Jun 19, 2020
2017cf7
better.
dzearing Jun 19, 2020
bc73216
Fixing unneeded style prop in button.
dzearing Jun 19, 2020
adf85fb
undo
dzearing Jun 19, 2020
be6f90c
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
dzearing Jun 19, 2020
a812385
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
dzearing Jun 19, 2020
8ef4d6d
Change files
dzearing Jun 19, 2020
b50816a
update.
dzearing Jun 19, 2020
3bcf995
cleanup.
dzearing Jun 19, 2020
01b92e9
Removing oufr dependency from button, updating api
dzearing Jun 19, 2020
dac3318
Update change/@fluentui-react-button-2020-06-19-15-32-16-fix-button-f…
dzearing Jun 19, 2020
e62f1a6
Update packages/react-button/src/components/Button/Button.types.tsx
dzearing Jun 19, 2020
dc229ae
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
dzearing Jun 24, 2020
6ba907c
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
dzearing Jun 29, 2020
34a25c0
updates.
dzearing Jun 29, 2020
6737aa7
This is wrong - styles should not be injected without being necessary.
dzearing Jun 29, 2020
70d3271
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
dzearing Jun 29, 2020
cc9ad5d
undo!
dzearing Jun 29, 2020
04bce6d
no message
dzearing Jun 29, 2020
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "minor",
"comment": "Button: Adding focus method and tests.",
dzearing marked this conversation as resolved.
Show resolved Hide resolved
"packageName": "@fluentui/react-button",
"email": "dzearing@microsoft.com",
"dependentChangeType": "patch",
"date": "2020-06-19T22:32:16.380Z"
}
25 changes: 15 additions & 10 deletions packages/react-button/src/components/Button/Button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { Stack, Text } from 'office-ui-fabric-react';

const ButtonVariants = (props: ButtonProps) => (
<div className={classes.hStack}>
<Button {...props} content="Hello, world" icon="X" />
<Button {...props} primary content="Hello, world" icon="X" />
<Button {...props} disabled content="Hello, world" icon="X" />
<Button {...props} primary disabled content="Hello, world" icon="X" />
<Button content={'Hello, world'} icon="O" {...props} />
<Button primary content="Hello, world" icon="X" {...props} />
<Button disabled content="Hello, world" icon="X" {...props} />
<Button primary disabled content="Hello, world" icon="X" {...props} />
</div>
);

Expand Down Expand Up @@ -149,7 +149,6 @@ export const ButtonTokens = () => (
fontFamily: 'Circular, Helvetica, Arial, sans-serif',
fontSize: '14px',
fontWeight: '700',

background: 'rgb(29, 185, 84) none repeat scroll 0% 0% / auto padding-box border-box',
contentColor: 'rgb(255, 255, 255)',
borderColor: 'rgb(255, 255, 255)',
Expand Down Expand Up @@ -187,19 +186,25 @@ export const ButtonTokens = () => (
</div>
<Text variant="xLarge">A tokenized button can be customized for any size or padding.</Text>
<div className={classes.vStack}>
<ButtonVariants
<Button
content="I'm a small button with a large icon"
icon="O"
tokens={{
height: '24px',
fontSize: '12px',
iconSize: '22px',
iconSize: '40px',
padding: '0 8px',
contentGap: '4px',
}}
/>
<ButtonVariants
<Button
content="I'm a large button with a small icon"
icon="O"
tokens={{
height: '70px',
fontSize: '48px',
iconSize: '48px',
fontSize: '24px',
iconSize: '12px',
padding: '0 40px',
}}
/>
</div>
Expand Down
36 changes: 36 additions & 0 deletions packages/react-button/src/components/Button/Button.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<Button content="Default button" />);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});

it('can be focused', () => {
const rootRef = React.createRef<HTMLButtonElement>();
const componentRef = React.createRef<ButtonRef>();

wrapper = mount(<Button ref={rootRef} componentRef={componentRef} content="Focus me" />);

expect(typeof rootRef.current).toEqual('object');
expect(document.activeElement).not.toEqual(rootRef.current);

componentRef.current?.focus();

expect(document.activeElement).toEqual(rootRef.current);
});
});
21 changes: 19 additions & 2 deletions packages/react-button/src/components/Button/Button.types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,22 @@ import { ColorPlateSet } from '@fluentui/react-theme-provider';

export type SizeValue = 'smallest' | 'smaller' | 'small' | 'medium' | 'large' | 'larger' | 'largest';

/**
* {@docCategory Button}
*/
export interface ButtonRef {
/**
* Sets focus to the button.
*/
focus: () => void;
}

export interface ButtonProps extends ComponentProps, React.HTMLAttributes<HTMLButtonElement> {
/**
* Access the imperative api of the button.
dzearing marked this conversation as resolved.
Show resolved Hide resolved
*/
componentRef?: React.RefObject<ButtonRef>;

/**
* Shorthand icon. A shorthand prop can be a literal, object,
* JSX, or function which takes render options.
Expand Down Expand Up @@ -34,7 +49,7 @@ export interface ButtonProps extends ComponentProps, React.HTMLAttributes<HTMLBu
/** A button can contain only an icon. */
iconOnly?: boolean;

/** 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. */
iconPosition?: 'before' | 'after';

/** A button that inherits its background and has a subtle appearance. */
Expand Down Expand Up @@ -73,7 +88,9 @@ export interface ButtonProps extends ComponentProps, React.HTMLAttributes<HTMLBu
tokens?: RecursivePartial<ButtonTokens>;
}

export interface ButtonState extends ButtonProps {}
export interface ButtonState extends ButtonProps {
buttonRef?: React.RefObject<HTMLButtonElement>;
}

export interface ButtonSlots {
icon: React.ElementType;
Expand Down
9 changes: 7 additions & 2 deletions packages/react-button/src/components/Button/ButtonBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import * as React from 'react';
import { ButtonProps } from './Button.types';
import { compose, mergeProps } from '@fluentui/react-compose';
import { useButton } from './useButton';
import { useMergedRefs } from '@uifabric/react-hooks';

export const ButtonBase = compose<'button', ButtonProps, ButtonProps, {}, {}>(
(props, ref, options) => {
const { state } = options;
const { slots, slotProps } = mergeProps(state, options);

return (
<slots.root ref={ref} {...slotProps.root}>
<slots.root ref={useMergedRefs(ref, state.buttonRef)} {...slotProps.root}>
dzearing marked this conversation as resolved.
Show resolved Hide resolved
{props.loading && <slots.loader {...slotProps.loader} />}
{props.icon && props.iconPosition !== 'after' && <slots.icon {...slotProps.icon} />}
{!props.iconOnly && props.content && <slots.content {...slotProps.content} />}
Expand All @@ -20,6 +21,8 @@ export const ButtonBase = compose<'button', ButtonProps, ButtonProps, {}, {}>(
{
displayName: 'ButtonBase',
handledProps: [
'buttonRef',
'componentRef',
'circular',
'content',
'disabled',
Expand All @@ -31,8 +34,10 @@ export const ButtonBase = compose<'button', ButtonProps, ButtonProps, {}, {}>(
'loading',
'primary',
'secondary',
'tokens',
'size',
],
// tslint:disable-next-line:no-any
] as any,
dzearing marked this conversation as resolved.
Show resolved Hide resolved
slots: {
icon: 'span',
content: 'span',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import * as React from 'react';
import { Button } from './Button';
import { mergeThemes, ThemeProvider, Theme } from '@fluentui/react-theme-provider';
import { mergeThemes, ThemeProvider, Theme, PartialTheme } from '@fluentui/react-theme-provider';
import { UploadIcon } from '@fluentui/react-icons';
import { Stack, Text, ColorPicker, IColor } from 'office-ui-fabric-react';

// tslint:disable: jsx-ban-props
const paletteAccent = 'var(--palette-accent)';
const paletteSoftest = 'var(--palette-softest)';
const paletteStrongest = 'var(--palette-strongest)';

const getThemes = (accent: string) => {
const lightTheme = mergeThemes({
tokens: {
palette: {
accent,
accent: accent,
softest: 'white',
strongest: 'black',
},

body: {
Expand All @@ -19,7 +24,17 @@ const getThemes = (accent: string) => {
},

accent: {
background: 'var(--palette-accent)',
background: paletteAccent,
contentColor: paletteSoftest,

hovered: {
background: paletteAccent,
contentColor: paletteSoftest,
},
pressed: {
background: paletteAccent,
contentColor: paletteSoftest,
},
},

button: {
Expand All @@ -31,19 +46,37 @@ const getThemes = (accent: string) => {

const darkTheme = mergeThemes(lightTheme, {
tokens: {
palette: {
softest: 'black',
strongest: 'white',
},

body: {
background: '#333',
contentColor: 'white',
contentColor: paletteStrongest,
},

button: {
background: 'transparent',
contentColor: 'white',
contentColor: paletteStrongest,

hovered: {
background: '#555',
contentColor: paletteStrongest,
},
},

accent: {
background: 'blue',
contentColor: paletteStrongest,

hovered: {
background: '#555',
contentColor: paletteStrongest,
},
},
},
});
} as PartialTheme);

return { lightTheme, darkTheme };
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Button renders a default state 1`] = `
<button>
<span>
Default button
</span>
</button>
`;
23 changes: 22 additions & 1 deletion packages/react-button/src/components/Button/useButton.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
import { useImperativeHandle, useRef } from 'react';
import { tokensToStyleObject } from '@fluentui/react-theme-provider';
import { ButtonProps, ButtonState } from './Button.types';

const getStyle = (props: ButtonProps) => {
if (props.style || props.tokens) {
return {
...props.style,
...tokensToStyleObject(props.tokens, '--button'),
};
}

return undefined;
};

/**
* The useButton hook processes the Button component props and returns state.
* @param props - Button props to derive state from.
*/
export const useButton = (props: ButtonProps): ButtonState => {
const buttonRef = useRef<HTMLButtonElement | null>(null);

useImperativeHandle(props.componentRef, () => ({
focus: () => {
buttonRef.current?.focus();
},
}));

return {
...props,
style: { ...props.style, ...tokensToStyleObject(props.tokens, '--button') },
buttonRef,
style: getStyle(props),
};
};