diff --git a/semcore/spin-container/__tests__/index.test.tsx b/semcore/spin-container/__tests__/index.test.tsx
index ed88eecc8c..df199fbeab 100644
--- a/semcore/spin-container/__tests__/index.test.tsx
+++ b/semcore/spin-container/__tests__/index.test.tsx
@@ -22,4 +22,17 @@ describe('SpinContainer', () => {
const { container } = render(spinContainer);
expect(extractUIName(container)).toMatchSnapshot();
});
+
+ test('Verify Overlay falls back to Spin when children are explicitly undefined', () => {
+ const { container } = render(
+
+ Content
+ {undefined}
+ ,
+ );
+
+ const overlay = container.querySelector('[data-ui-name="SpinContainer.Overlay"]');
+
+ expect(overlay?.querySelector('[data-ui-name="Spin"]')).not.toBeNull();
+ });
});
diff --git a/semcore/spin-container/__tests__/spin-container.browser-test.tsx b/semcore/spin-container/__tests__/spin-container.browser-test.tsx
index 2037c9d127..3b73b9420d 100644
--- a/semcore/spin-container/__tests__/spin-container.browser-test.tsx
+++ b/semcore/spin-container/__tests__/spin-container.browser-test.tsx
@@ -4,6 +4,8 @@ import { loadPage } from '@semcore/testing-utils/shared/helpers';
import { TAG } from '@semcore/testing-utils/shared/tags';
const ALL_SIZES_EXAMPLE = 'stories/components/spin-container/advanced/examples/all-sizes.tsx';
+const AUTOFOCUS_EXAMPLE =
+ 'stories/components/spin-container/tests/examples/spin-container-with-autofocus-input.tsx';
const BASE_EXAMPLE = 'stories/components/spin-container/tests/examples/spin-container-base.tsx';
const CONTENT_EXAMPLE = 'stories/components/spin-container/docs/examples/usage_in_content.tsx';
@@ -243,6 +245,36 @@ test.describe(`${TAG.FUNCTIONAL}`, () => {
await expect(locators.input(page, 0)).toBeFocused();
});
+ test('Verify autofocused content gets focus as soon as loading ends', {
+ tag: [TAG.PRIORITY_HIGH, TAG.KEYBOARD, '@spin-container',
+ '@base-components',
+ '@button',
+ '@input',
+ '@typography'],
+ }, async ({ page }) => {
+ await loadPage(page, AUTOFOCUS_EXAMPLE, 'en');
+
+ const input = page.getByRole('textbox', { name: 'Input' });
+
+ await test.step('Verify content is inert while loading', async () => {
+ await expect(locators.spinContainerContent(page)).toHaveAttribute('inert', '');
+ await expect(input).toHaveCount(0);
+ });
+
+ await test.step('Verify autofocused input is focused once loading stops', async () => {
+ await page.getByRole('button', { name: 'Stop loading' }).click();
+
+ await expect(input).toBeFocused();
+ });
+
+ await test.step('Verify content becomes inert again when loading restarts', async () => {
+ await page.getByRole('button', { name: 'Start loading' }).click();
+
+ await expect(locators.spinContainerContent(page)).toHaveAttribute('inert', '');
+ await expect(input).toHaveCount(0);
+ });
+ });
+
test('Verify size and theme are forwarded to the nested Spin', {
tag: [TAG.PRIORITY_HIGH, '@spin-container',
'@base-components',
diff --git a/semcore/spin-container/src/SpinContainer.tsx b/semcore/spin-container/src/SpinContainer.tsx
index 4db7410f00..bd361dbe71 100644
--- a/semcore/spin-container/src/SpinContainer.tsx
+++ b/semcore/spin-container/src/SpinContainer.tsx
@@ -9,16 +9,12 @@ import React from 'react';
import type { NSSpinContainer } from './SpinContainer.type';
import style from './style/spin-container.shadow.css';
-type State = {
- inert?: boolean;
-};
-
class SpinContainerRoot extends Component<
Intergalactic.InternalTypings.InferComponentProps,
typeof SpinContainerRoot.enhance,
{},
{},
- State,
+ {},
NSSpinContainer.DefaultProps
> {
static displayName = 'SpinContainer';
@@ -31,29 +27,6 @@ class SpinContainerRoot extends Component<
static enhance = [resolveColorEnhance()] as const;
- state: State = {
- inert: this.props.loading,
- };
-
- private inertTimer: ReturnType | null = null;
-
- componentDidUpdate(prevProps: typeof this.asProps) {
- const { loading } = this.props;
- if (prevProps.loading !== loading) {
- if (this.inertTimer) {
- clearTimeout(this.inertTimer);
- }
-
- if (loading) {
- this.setState({ inert: true });
- } else {
- this.inertTimer = setTimeout(() => {
- this.setState({ inert: false });
- }, this.asProps.duration + 50);
- }
- }
- }
-
getOverlayProps() {
const { loading, background, duration, size, theme, resolveColor } = this.asProps;
return {
@@ -69,9 +42,11 @@ class SpinContainerRoot extends Component<
}
getContentProps() {
- const { inert } = this.state;
+ const { loading } = this.asProps;
- return { inert: inert ? '' : undefined };
+ return {
+ inert: loading ? '' : undefined,
+ };
}
render() {
@@ -103,18 +78,24 @@ class SpinContainerRoot extends Component<
type OverlayProps = Intergalactic.InternalTypings.InferChildComponentProps;
class Overlay extends Component {
- static defaultProps = ({ size, theme }: OverlayProps) => ({
- children: ,
- });
-
render() {
const SOverlay = Root;
- const { styles, theme, background, loading, duration, resolveColor } = this.asProps;
+ const { styles, theme, size, background, loading, duration, resolveColor, children: hasChildren, Children } = this.asProps;
const useTheme = background ? 'custom' : theme;
return sstyled(styles)(
-
+
+ {hasChildren
+ ?
+ : (
+
+ )}
+
,
);
}
diff --git a/stories/components/spin-container/tests/examples/spin-container-with-autofocus-input.tsx b/stories/components/spin-container/tests/examples/spin-container-with-autofocus-input.tsx
new file mode 100644
index 0000000000..39509e7d57
--- /dev/null
+++ b/stories/components/spin-container/tests/examples/spin-container-with-autofocus-input.tsx
@@ -0,0 +1,33 @@
+import { Flex } from '@semcore/ui/base-components';
+import Button from '@semcore/ui/button';
+import Input from '@semcore/ui/input';
+import SpinContainer from '@semcore/ui/spin-container';
+import { Text } from '@semcore/ui/typography';
+import React from 'react';
+
+const Demo = () => {
+ const [loading, setLoading] = React.useState(true);
+
+ return (
+
+
+
+
+ {!loading && (
+
+
+ Input
+
+
+
+
+
+ )}
+
+
+ );
+};
+
+export default Demo;
diff --git a/stories/components/spin-container/tests/spin-container.stories.tsx b/stories/components/spin-container/tests/spin-container.stories.tsx
index 88611a6b46..91e8ab9f81 100644
--- a/stories/components/spin-container/tests/spin-container.stories.tsx
+++ b/stories/components/spin-container/tests/spin-container.stories.tsx
@@ -4,6 +4,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite';
import SpinContainerBaseExample, {
defaultSpinContainerProps,
} from './examples/spin-container-base';
+import SpinContainerWithAutofocusInputExample from './examples/spin-container-with-autofocus-input';
const meta: Meta = {
title: 'Components/SpinContainer/Tests',
@@ -62,3 +63,7 @@ export const SpinContainerBase: StoryObj = {
argTypes: spinContainerArgTypes,
args: defaultSpinContainerProps,
};
+
+export const SpinContainerWithAutofocusInput = {
+ render: SpinContainerWithAutofocusInputExample,
+};