Skip to content

Commit

Permalink
[Joy] Add button loading functionality (#34658)
Browse files Browse the repository at this point in the history
  • Loading branch information
kushagra010 committed Oct 10, 2022
1 parent 13ff52c commit daed91d
Show file tree
Hide file tree
Showing 12 changed files with 330 additions and 6 deletions.
17 changes: 17 additions & 0 deletions docs/data/joy/components/button/ButtonLoading.js
@@ -0,0 +1,17 @@
import * as React from 'react';
import Stack from '@mui/joy/Stack';
import SendIcon from '@mui/icons-material/Send';
import Button from '@mui/joy/Button';

export default function ButtonLoading() {
return (
<Stack spacing={2} direction="row">
<Button loading endDecorator={<SendIcon />} variant="solid">
Send
</Button>
<Button loading loadingIndicator="Loading…" variant="outlined">
Fetch data
</Button>
</Stack>
);
}
17 changes: 17 additions & 0 deletions docs/data/joy/components/button/ButtonLoading.tsx
@@ -0,0 +1,17 @@
import * as React from 'react';
import Stack from '@mui/joy/Stack';
import SendIcon from '@mui/icons-material/Send';
import Button from '@mui/joy/Button';

export default function ButtonLoading() {
return (
<Stack spacing={2} direction="row">
<Button loading endDecorator={<SendIcon />} variant="solid">
Send
</Button>
<Button loading loadingIndicator="Loading…" variant="outlined">
Fetch data
</Button>
</Stack>
);
}
6 changes: 6 additions & 0 deletions docs/data/joy/components/button/ButtonLoading.tsx.preview
@@ -0,0 +1,6 @@
<Button loading endDecorator={<SendIcon />} variant="solid">
Send
</Button>
<Button loading loadingIndicator="Loading…" variant="outlined">
Fetch data
</Button>
22 changes: 22 additions & 0 deletions docs/data/joy/components/button/ButtonLoadingPosition.js
@@ -0,0 +1,22 @@
import * as React from 'react';
import Stack from '@mui/joy/Stack';
import SendIcon from '@mui/icons-material/Send';
import Button from '@mui/joy/Button';

export default function ButtonLoadingPosition() {
return (
<Stack spacing={2} direction="row">
<Button loading loadingPosition="start" variant="outlined">
Fetch data
</Button>
<Button
loading
loadingPosition="end"
endDecorator={<SendIcon />}
variant="solid"
>
Send
</Button>
</Stack>
);
}
22 changes: 22 additions & 0 deletions docs/data/joy/components/button/ButtonLoadingPosition.tsx
@@ -0,0 +1,22 @@
import * as React from 'react';
import Stack from '@mui/joy/Stack';
import SendIcon from '@mui/icons-material/Send';
import Button from '@mui/joy/Button';

export default function ButtonLoadingPosition() {
return (
<Stack spacing={2} direction="row">
<Button loading loadingPosition="start" variant="outlined">
Fetch data
</Button>
<Button
loading
loadingPosition="end"
endDecorator={<SendIcon />}
variant="solid"
>
Send
</Button>
</Stack>
);
}
11 changes: 11 additions & 0 deletions docs/data/joy/components/button/ButtonLoadingPosition.tsx.preview
@@ -0,0 +1,11 @@
<Button loading loadingPosition="start" variant="outlined">
Fetch data
</Button>
<Button
loading
loadingPosition="end"
endDecorator={<SendIcon />}
variant="solid"
>
Send
</Button>
18 changes: 18 additions & 0 deletions docs/data/joy/components/button/button.md
Expand Up @@ -70,6 +70,24 @@ Use the `startDecorator` and/or `endDecorator` props to add supporting decorator

{{"demo": "ButtonIcons.js"}}

### Loading

Enable `loading` prop to show button's loading state. The button will be `disabled` when it is in the loading state.

The default loading indicator uses the [`CircularProgress`](/joy-ui/react-circular-progress/) component which can be customized using the `loadingIndicator` prop.

{{"demo": "ButtonLoading.js"}}

### Loading position

The `loadingPosition` prop supports 3 values:

- `center` (default): The loading indicator element is wrapped inside the button's `loadingIndicatorCenter` slot to create a proper style.
- `start`: The loading indicator replaces the **start** decorator's content when the button is in loading state.
- `end`: The loading indicator replaces the **end** decorator's content when the button is in loading state.

{{"demo": "ButtonLoadingPosition.js"}}

### Icon button

Use the `IconButton` component if you want width and height to be the same while not having a label.
Expand Down
13 changes: 13 additions & 0 deletions packages/mui-joy/src/Button/Button.spec.tsx
Expand Up @@ -84,3 +84,16 @@ function Icon() {
<Button variant="outlined" endDecorator={<Icon />} color="success">
Checkout
</Button>;

<Button loading variant="outlined" disabled>
disabled
</Button>;
<Button loading loadingIndicator="Loading…" variant="outlined">
Fetch data
</Button>;
<Button endDecorator={<Icon />} loading loadingPosition="end">
Send
</Button>;
<Button loading loadingPosition="start" startDecorator={<Icon />}>
Save
</Button>;
96 changes: 96 additions & 0 deletions packages/mui-joy/src/Button/Button.test.js
Expand Up @@ -93,4 +93,100 @@ describe('Joy <Button />', () => {
expect(button).to.have.class(classes.root);
expect(endDecorator).not.to.have.class(classes.startDecorator);
});

describe('prop: loading', () => {
it('disables the button', () => {
const { getByRole } = render(<Button loading />);

const button = getByRole('button');
expect(button).to.have.property('disabled', true);
});

it('renders a progressbar', () => {
const { getByRole } = render(<Button loading>Submit</Button>);

const progressbar = getByRole('progressbar');
expect(progressbar).toBeVisible();
});
});

describe('prop: loadingIndicator', () => {
it('is not rendered by default', () => {
const { getByRole } = render(<Button loadingIndicator="loading">Test</Button>);

expect(getByRole('button')).to.have.text('Test');
});

it('is rendered properly when `loading` and children should not be visible', function test() {
if (!/jsdom/.test(window.navigator.userAgent)) {
this.skip();
}
const { container, getByRole } = render(
<Button loadingIndicator="loading.." loading>
Test
</Button>,
);

expect(container.querySelector(`.${classes.loadingIndicatorCenter}`)).to.have.text(
'loading..',
);
expect(getByRole('button')).toHaveComputedStyle({ color: 'transparent' });
});
});

describe('prop: loadingPosition', () => {
it('center is rendered by default', () => {
const { getByRole } = render(<Button loading>Test</Button>);
const loader = getByRole('progressbar');

expect(loader.parentElement).to.have.class(classes.loadingIndicatorCenter);
});

it('there should be only one loading indicator', () => {
const { getAllByRole } = render(
<Button loading startDecorator="🚀" endDecorator="👍">
Test
</Button>,
);
const loaders = getAllByRole('progressbar');

expect(loaders).to.have.length(1);
});

it('loading indicator with `position="start"` replaces the `startDecorator` content', () => {
const { getByRole } = render(
<Button
loading
startDecorator={<span>icon</span>}
loadingPosition="start"
loadingIndicator={<span role="progressbar">loading..</span>}
>
Test
</Button>,
);
const loader = getByRole('progressbar');
const button = getByRole('button');

expect(loader).toBeVisible();
expect(button).to.have.text('loading..Test');
});

it('loading indicator with `position="end"` replaces the `startDecorator` content', () => {
const { getByRole } = render(
<Button
loading
endDecorator={<span>icon</span>}
loadingPosition="end"
loadingIndicator={<span role="progressbar">loading..</span>}
>
Test
</Button>,
);
const loader = getByRole('progressbar');
const button = getByRole('button');

expect(loader).toBeVisible();
expect(button).to.have.text('Testloading..');
});
});
});

0 comments on commit daed91d

Please sign in to comment.