Commit f0198dc0ce for woocommerce
commit f0198dc0ceac44be11e3c38266260429abd6798a
Author: Chi-Hsuan Huang <chihsuan.tw@gmail.com>
Date: Mon Jan 12 11:13:50 2026 +0900
Fix Import Status Bar UI overlap and add loading state (#62675)
* Enhance Import Status Bar UI and Logic
* Update styles for the import status bar to improve alignment and responsiveness.
* Refactor button logic to use a single 'isBusy' state for better clarity.
* Adjust button display to show a spinner when busy, enhancing user feedback.
* Update Import Status Bar and Report Header Styles
* Add width to the import status bar label for improved layout.
* Enhance accessibility by adding aria attributes to the import trigger button.
* Update the aria-label to reflect the import status dynamically.
* Adjust media query breakpoint in report header styles for better responsiveness.
* Add changefile(s) from automation for the following project(s): woocommerce, woocommerce/client/admin
* Update import status bar styles to prevent layout shift during loading
* Add a comment to clarify the purpose of setting a fixed width for the import status bar label.
* Update import status bar styles for improved responsiveness
* Change gap values in media queries to use defined variables for better consistency across breakpoints.
* Fix unit tests for accessibility improvements
Update tests to use new dynamic aria-label when button is busy:
- "Analytics data import in progress" instead of "Manually trigger analytics data import"
- Add assertion for aria-busy attribute
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix E2E test for updated button aria-label
Update the analytics-overview E2E test to use the correct button
aria-label after accessibility improvements. The button now has:
- Initial state: "Manually trigger analytics data import"
- Busy state: "Analytics data import in progress"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/62675-fix-scheduled-import-ui b/plugins/woocommerce/changelog/62675-fix-scheduled-import-ui
new file mode 100644
index 0000000000..bcda2c5081
--- /dev/null
+++ b/plugins/woocommerce/changelog/62675-fix-scheduled-import-ui
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix Import Status Bar UI overlap with filter dropdowns and add loading state feedback
\ No newline at end of file
diff --git a/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/import-status-bar.scss b/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/import-status-bar.scss
index bf13a265fd..2d1e38084e 100644
--- a/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/import-status-bar.scss
+++ b/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/import-status-bar.scss
@@ -9,11 +9,21 @@
gap: 6px;
&__label {
+ margin: 7px 0 0; // use same margin as filters to they align vertically
font-size: 13px;
line-height: 20px;
color: $gray-900;
font-weight: 400;
}
+
+ @media screen and ( max-width: $break-large ) {
+ gap: 5px;
+
+ &__label {
+ line-height: 18px;
+ margin-top: 5px;
+ }
+ }
}
.woocommerce-analytics-import-status-bar {
@@ -34,7 +44,11 @@
@media screen and ( max-width: $break-wide ) {
- gap: 24px;
+ gap: $gap-large;
+ }
+
+ @media screen and ( max-width: $break-large ) {
+ gap: $gap;
}
}
@@ -56,6 +70,8 @@
font-size: 11px;
font-weight: 500;
line-height: 20px;
+ // Set a fixed width to prevent layout shift during loading
+ width: 88px;
}
.components-spinner {
diff --git a/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/import-status-bar.tsx b/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/import-status-bar.tsx
index 2a597f92bf..ee76f8d8bf 100644
--- a/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/import-status-bar.tsx
+++ b/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/import-status-bar.tsx
@@ -109,9 +109,7 @@ export function ImportStatusBar(): JSX.Element | null {
}
};
- // Disable button when an import is already scheduled/running or currently triggering
- const isButtonDisabled =
- status?.import_in_progress_or_due || isTriggeringImport;
+ const isBusy = status?.import_in_progress_or_due || isTriggeringImport;
return (
<div className="woocommerce-analytics-import-status-bar-wrapper">
@@ -157,15 +155,27 @@ export function ImportStatusBar(): JSX.Element | null {
<Button
variant="tertiary"
onClick={ handleTriggerImport }
- disabled={ isButtonDisabled || isLoading }
- isBusy={ isTriggeringImport }
+ disabled={ isLoading || isBusy }
+ aria-disabled={ isLoading || isBusy }
+ aria-busy={ isBusy }
className="woocommerce-analytics-import-status-bar__trigger"
- aria-label={ __(
- 'Manually trigger analytics data import',
- 'woocommerce'
- ) }
+ aria-label={
+ isBusy
+ ? __(
+ 'Analytics data import in progress',
+ 'woocommerce'
+ )
+ : __(
+ 'Manually trigger analytics data import',
+ 'woocommerce'
+ )
+ }
>
- { __( 'Update now', 'woocommerce' ) }
+ { isBusy ? (
+ <Spinner />
+ ) : (
+ __( 'Update now', 'woocommerce' )
+ ) }
</Button>
</div>
</div>
diff --git a/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/test/import-status-bar.test.tsx b/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/test/import-status-bar.test.tsx
index f9c87966e8..3a3ff7f9d0 100644
--- a/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/test/import-status-bar.test.tsx
+++ b/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/test/import-status-bar.test.tsx
@@ -138,10 +138,12 @@ describe( 'ImportStatusBar', () => {
render( <ImportStatusBar /> );
+ // When busy, aria-label changes to "Analytics data import in progress"
const button = screen.getByRole( 'button', {
- name: /Manually trigger analytics data import/i,
+ name: /Analytics data import in progress/i,
} );
expect( button ).toBeDisabled();
+ expect( button ).toHaveAttribute( 'aria-busy', 'true' );
} );
it( 'should disable button when isTriggeringImport is true', () => {
@@ -153,10 +155,12 @@ describe( 'ImportStatusBar', () => {
render( <ImportStatusBar /> );
+ // When busy, aria-label changes to "Analytics data import in progress"
const button = screen.getByRole( 'button', {
- name: /Manually trigger analytics data import/i,
+ name: /Analytics data import in progress/i,
} );
expect( button ).toBeDisabled();
+ expect( button ).toHaveAttribute( 'aria-busy', 'true' );
} );
it( 'should trigger import on button click', async () => {
diff --git a/plugins/woocommerce/client/admin/client/analytics/components/report-header/report-header.scss b/plugins/woocommerce/client/admin/client/analytics/components/report-header/report-header.scss
index 612b247a34..d98716daf5 100644
--- a/plugins/woocommerce/client/admin/client/analytics/components/report-header/report-header.scss
+++ b/plugins/woocommerce/client/admin/client/analytics/components/report-header/report-header.scss
@@ -5,38 +5,53 @@
*/
.woocommerce-analytics-report-header {
- position: relative;
- display: flex;
- align-items: flex-start;
+ // Only apply this layout when the import status bar is present
+ &:has(> .woocommerce-analytics-import-status-bar-wrapper) {
+ position: relative;
+ display: flex;
+ align-items: flex-start;
+ gap: $gap;
- .woocommerce-filters {
- flex: 1; // Allow the filters to grow and take up remaining space
- width: 100%;
- }
+ .woocommerce-filters {
+ flex: 1; // Allow the filters to grow and take up remaining space
+ width: 100%;
+ }
- .woocommerce-analytics-import-status-bar-wrapper {
- // Absolute positioning ensures the filters’ width is independent from the status bar, which can change its size dynamically as needed
- position: absolute;
- right: 0;
- }
+ .woocommerce-filters-filter {
+ min-width: fit-content;
+ }
- @media screen and ( max-width: $break-large ) {
- flex-direction: column;
+ @media screen and ( max-width: $break-wide ) {
+ .woocommerce-filters-filter {
+ width: 100%;
+ }
- .woocommerce-analytics-import-status-bar-wrapper {
- position: relative;
- right: auto;
+ .woocommerce-analytics-import-status-bar-wrapper {
+ flex: 1;
+ }
}
- }
- @media screen and ( max-width: $break-medium ) {
- .woocommerce-analytics-import-status-bar-wrapper,
- .woocommerce-analytics-import-status-bar__content {
- width: 100%;
+ @media screen and ( max-width: $break-medium ) {
+ flex-direction: column;
+ gap: 0;
+
+ .woocommerce-filters__basic-filters {
+ margin-bottom: $gap;
+ }
+
+ .woocommerce-analytics-import-status-bar-wrapper {
+ margin-bottom: $gap-large;
+ width: 100%;
+ }
+
+ .woocommerce-analytics-import-status-bar__content {
+ width: 100%;
+ justify-content: space-between;
+ }
}
- .woocommerce-analytics-import-status-bar__content {
- justify-content: space-between;
+ @media screen and ( max-width: $break-small ) {
+ align-items: center;
}
}
}
diff --git a/plugins/woocommerce/tests/e2e-pw/tests/analytics/analytics-overview.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/analytics/analytics-overview.spec.js
index 654afe4f2e..91e20bd5c1 100644
--- a/plugins/woocommerce/tests/e2e-pw/tests/analytics/analytics-overview.spec.js
+++ b/plugins/woocommerce/tests/e2e-pw/tests/analytics/analytics-overview.spec.js
@@ -357,7 +357,7 @@ test.describe(
await expect( page.getByText( 'Data status:' ) ).toBeVisible();
// Verify "Update now" button is visible
const updateButton = page.getByRole( 'button', {
- name: 'Manually trigger analytics',
+ name: 'Manually trigger analytics data import',
} );
await expect( updateButton ).toBeVisible();
@@ -373,7 +373,11 @@ test.describe(
await updateButton.click();
// Verify button shows loading state (isBusy)
- await expect( updateButton ).toBeDisabled();
+ // After clicking, the aria-label changes to "Analytics data import in progress"
+ const busyButton = page.getByRole( 'button', {
+ name: 'Analytics data import in progress',
+ } );
+ await expect( busyButton ).toBeDisabled();
// Wait for API response
await responsePromise;