Skip to content

Initial Spinner Implementation#22207

Merged
tomi-msft merged 21 commits into
microsoft:masterfrom
tomi-msft:react-spinner-implementation
Apr 1, 2022
Merged

Initial Spinner Implementation#22207
tomi-msft merged 21 commits into
microsoft:masterfrom
tomi-msft:react-spinner-implementation

Conversation

@tomi-msft

@tomi-msft tomi-msft commented Mar 24, 2022

Copy link
Copy Markdown
Contributor

This PR is for the initial implementation of the v9 Spinner

Related Issue(s)

Fixes #21399

@codesandbox-ci

codesandbox-ci Bot commented Mar 24, 2022

Copy link
Copy Markdown

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit a83054e:

Sandbox Source
@fluentui/react 8 starter Configuration
@fluentui/react-components 9 starter Configuration

@fabricteam

fabricteam commented Mar 24, 2022

Copy link
Copy Markdown
Collaborator

📊 Bundle size report

Package & Exports Baseline (minified/GZIP) PR Change
react-spinner
Spinner
6.815 kB
2.9 kB
16.459 kB
5.549 kB
9.644 kB
2.649 kB

🤖 This report was generated against 374dca7b9d662f71a1f81297d7e4f4ea95615645

@size-auditor

size-auditor Bot commented Mar 24, 2022

Copy link
Copy Markdown

Asset size changes

Size Auditor did not detect a change in bundle size for any component!

Baseline commit: 374dca7b9d662f71a1f81297d7e4f4ea95615645 (build)

@tomi-msft
tomi-msft marked this pull request as ready for review March 28, 2022 16:58
@tomi-msft
tomi-msft requested a review from a team as a code owner March 28, 2022 16:58
disabledTests: [
'component-has-static-classname',
'component-has-static-classname-exported',
'component-has-static-classnames-object',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe Spinner should be exporting the classnames object covered by this conformance test.

return (
<slots.root {...slotProps.root}>
{slots.label && (labelPosition === 'above' || labelPosition === 'before') && <slots.label {...slotProps.label} />}
{slots.spinner && <slots.spinner {...slotProps.spinner} />}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the spinner slot should be required as it's the core feature of this component. If so, it should be marked as NonNullable in the .types.ts file.

*/
export const useSpinner_unstable = (props: SpinnerProps, ref: React.Ref<HTMLElement>): SpinnerState => {
// Props
const { appearance = 'primary', labelPosition, size = 'medium', status = 'active' } = props;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the design have a default labelPosition? Seems like we should probably set one here because with it being undefined like this it will prevent labels from rendering unless a user sets labelPosition.

export * from './renderSpinner';
export * from './useSpinner';
export * from './useSpinnerStyles';
export * from './DefaultSvg';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's your thinking about exporting this SVG? My initial reaction is that we should not export it and keep it an internal implementation detail. For example, we might want to move away from using an SVG for the animation.

/**
* The status of the Spinner. Defaults to active
*/
status?: 'active' | 'inactive';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is status different from disabled?

animationTimingFunction: 'linear',
backgroundColor: 'transparent',
},
[`& > svg > circle`]: {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about selecting elements based on class name rather than tag name?


// global class for Spinner track
spinnerTrack: {
[`& > svg > circle#track`]: {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not use ids as there may be multiple Spinners on a page and users may reasonably use an id like track. Using an id in a selector also makes styles harder to override because of their higher specificity.

// modifier class for Spinner track if appearance="inverted"
spinnerTrackInverted: {
[`& > svg > circle#track`]: {
stroke: 'rgba(255,255,255,0.2)',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a token here?

Comment thread packages/react-spinner/src/components/Spinner/useSpinnerStyles.ts
Comment thread packages/react-spinner/src/components/Spinner/useSpinnerStyles.ts
disabledTests: [
'component-has-static-classname',
'component-has-static-classname-exported',
'component-has-static-classnames-object',

@sopranopillow sopranopillow Mar 28, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you are exporting the classnames object, this test should pass and shouldn't be disabled. Only component-has-static-classname should be disabled.

Comment on lines +8 to +38
spinner: 'fui-Spinner__spinner',
label: 'fui-Spinner__label',
};

const rvalues = {
tiny: '9',
extraSmall: '11',
small: '13',
medium: '14.5',
large: '16.5',
extraLarge: '18.5',
huge: '20',
};
// Sizes for the Spinner
const spinnnerSizes = {
tiny: '20px',
extraSmall: '24px',
small: '28px',
medium: '32px',
large: '36px',
extraLarge: '40px',
huge: '44px',
};

// Stroke widths for the Spinner
const spinnerStrokeWidth = {
sWidth: '2px',
mWidth: '3px',
lWidth: '4px',
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these sizes are going to be added to the theme tokens, maybe add a TODO mentioning they should be replaced once the tokens are added.

animationDuration: '1.5s',
animationIterationCount: 'infinite',
animationTimingFunction: 'cubic-bezier(0.33,0,0.67,1)',
//zIndex: 999,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this isn't needed, might want to remove the comment?

* The root of the Spinner.
* The root slot receives the `className` and `style` specified directly on the `<Spinner>`.
*/
root: Slot<'div'>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can you make this NonNullable?

const styles = useStyles();
return (
<div className={styles.container}>
<Spinner labelPosition="before" label={<Label>Label Position Before...</Label>} />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should just put the label text here. No need to the <Label> since the label slot renders that for you. Doing it the way you currently have results in nested <Label>s

Nested labels in dev tools

const useStyles = makeStyles({
container: {
'> div': { ...shorthands.padding('20px') },
'& label': { display: 'block', marginBottom: '10px' },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want to remove this style from the story as it's affecting the vertical positioning of the labels that are part of Spinner which misaligns the labels.

import * as React from 'react';

export const DefaultSvg = () => (
<svg role="progressbar">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomi-msft I think you're missing this change?

// TODO Add things shared between props and state here
/**
* The appearance of the Spinner.
* @defaultValue 'primary'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these @defaultValue should be @default based on this comment: #22096 (comment)

Comment on lines +5 to +6
<circle className="fui-spinner__Track" />
<circle className="fui-spinner__Tail" />

@sopranopillow sopranopillow Mar 31, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
<circle className="fui-spinner__Track" />
<circle className="fui-spinner__Tail" />
<circle className="fui-Spinner__Track" />
<circle className="fui-Spinner__Tail" />

Comment on lines +262 to +266
// style for label
spinnerLabel: {
color: tokens.colorNeutralForeground1,
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can remove this since label by default uses colorNeutralForeground1

Suggested change
// style for label
spinnerLabel: {
color: tokens.colorNeutralForeground1,
},

* Where the label is positioned relative to the Spinner
* @default 'after'
*/
labelPosition?: 'above' | 'below' | 'before' | 'after';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

above and below feel positional - like top and bottom. Consider only before and after and then a separate property about horizontal or vertical layout.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was discussed in the Spec PR to keep it this way to align with react-positioning: #21336 (comment)

* Spinner Props
*/
export type SpinnerProps = ComponentProps<SpinnerSlots> & SpinnerCommons;
export type SpinnerProps = Omit<ComponentProps<SpinnerSlots>, 'size'> & Partial<SpinnerCommons>;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is better to have commons have the required/optional based on its usage with SpinnerProps. Then have SpinnerState make required/optional changes. This way a caller reading the code doesn't have to do mental gymnastics to figure out what is required/option in props.


// modifier class for Spinner trail
spinnerTailInverted: {
[`& > svg > circle.fui-spinner__Tail`]: {

@GeoffCoxMSFT GeoffCoxMSFT Mar 31, 2022

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: You should use regular single quote instead of string template quotes unless you are injecting a value.

},
animationDuration: '1.5s',
animationIterationCount: 'infinite',
animationTimingFunction: 'cubic-bezier(0.33,0,0.67,1)',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider referencing constants like you do for body1 unless the tokens are now available.

disabledTests: ['component-has-static-classname', 'component-has-static-classname-exported'],
});

// TODO add more tests here, and create visual regression tests in /apps/vr-tests

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding the it('renders') test maybe with and without the label.

* TODO: Update with proper tokens when added
* Radii for the Spinner circles
*/
const rvalues = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: rValues

@tomi-msft
tomi-msft merged commit aa573af into microsoft:master Apr 1, 2022
@tomi-msft tomi-msft mentioned this pull request Apr 5, 2022
33 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Spinner Convergence

7 participants