Skip to content

Commit 784b5d2

Browse files
authored
fix: keep button content in order to maintain the width in loading state (#504)
1 parent 90e9061 commit 784b5d2

File tree

1 file changed

+40
-12
lines changed

1 file changed

+40
-12
lines changed

src/components/experimental/Button/Button.tsx

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import React, { ReactElement } from 'react';
1+
import React, { ReactElement, ReactNode } from 'react';
22
import styled from 'styled-components';
33
import { variant } from 'styled-system';
4-
import { Button as BaseButton, ButtonProps as BaseButtonProps } from 'react-aria-components';
4+
import { Button as BaseButton, ButtonProps as BaseButtonProps, ButtonRenderProps } from 'react-aria-components';
55
import { getSemanticValue } from '../../../essentials/experimental/cssVariables';
66
import { get } from '../../../utils/experimental/themeGet';
77
import { textStyles } from '../Text/Text';
@@ -74,14 +74,10 @@ const emphasisStyles = variant<Record<string, unknown>, Emphasis>({
7474
const ButtonStyled = styled(BaseButton)<{ $emphasis: Emphasis }>`
7575
position: relative;
7676
77-
display: inline-flex;
78-
align-items: center;
79-
justify-content: center;
80-
gap: ${get('space.2')};
8177
border: none;
8278
outline: none;
8379
border-radius: ${get('radii.4')};
84-
padding: ${get('space.4')} ${get('space.6')};
80+
padding: 0;
8581
8682
cursor: pointer;
8783
@@ -120,20 +116,52 @@ const ButtonStyled = styled(BaseButton)<{ $emphasis: Emphasis }>`
120116
${emphasisStyles};
121117
`;
122118

119+
const ChildrenContainer = styled.span<{ isLoading: boolean }>`
120+
display: inline-flex;
121+
align-items: center;
122+
justify-content: center;
123+
gap: ${get('space.2')};
124+
padding: ${get('space.4')} ${get('space.6')};
125+
126+
opacity: ${({ isLoading }) => (isLoading ? 0 : 1)};
127+
visibility: ${({ isLoading }) => (isLoading ? 'hidden' : 'visible')};
128+
transition: opacity ease 200ms;
129+
`;
130+
131+
const SpinnerContainer = styled.span`
132+
position: absolute;
133+
display: flex;
134+
justify-content: center;
135+
align-items: center;
136+
top: 50%;
137+
left: 50%;
138+
transform: translate(-50%, -50%);
139+
pointer-events: none;
140+
`;
141+
123142
const spinnerColor: Record<Emphasis, string> = {
124143
primary: getSemanticValue('on-accent'),
125144
secondary: getSemanticValue('on-surface'),
126145
textButton: getSemanticValue('on-surface')
127146
};
128147

129148
function Button({ children, emphasis = 'primary', isLoading = false, ...restProps }: ButtonProps): ReactElement {
149+
const renderContent = (props: ButtonRenderProps & { defaultChildren: ReactNode }) => (
150+
<>
151+
<ChildrenContainer isLoading={isLoading}>
152+
{typeof children === 'function' ? children(props) : children}
153+
</ChildrenContainer>
154+
{isLoading && (
155+
<SpinnerContainer>
156+
<InlineSpinner data-testid="button-spinner" color={spinnerColor[emphasis]} size="medium" />
157+
</SpinnerContainer>
158+
)}
159+
</>
160+
);
161+
130162
return (
131163
<ButtonStyled data-testid="button-container" isPending={isLoading} $emphasis={emphasis} {...restProps}>
132-
{isLoading ? (
133-
<InlineSpinner data-testid="button-spinner" color={spinnerColor[emphasis]} size="medium" />
134-
) : (
135-
children
136-
)}
164+
{renderContent}
137165
</ButtonStyled>
138166
);
139167
}

0 commit comments

Comments
 (0)