diff --git a/packages/js/product-editor/changelog/add-38489 b/packages/js/product-editor/changelog/add-38489
new file mode 100644
index 00000000000..0ab116b64de
--- /dev/null
+++ b/packages/js/product-editor/changelog/add-38489
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Add the list view component and button to the modal editor
diff --git a/packages/js/product-editor/src/components/iframe-editor/context.ts b/packages/js/product-editor/src/components/iframe-editor/context.ts
index ce1518769c8..7703387d989 100644
--- a/packages/js/product-editor/src/components/iframe-editor/context.ts
+++ b/packages/js/product-editor/src/components/iframe-editor/context.ts
@@ -6,8 +6,10 @@ import { createContext } from '@wordpress/element';
type EditorContextType = {
hasRedo: boolean;
hasUndo: boolean;
+ isDocumentOverviewOpened: boolean;
isInserterOpened: boolean;
redo: () => void;
+ setIsDocumentOverviewOpened: ( value: boolean ) => void;
setIsInserterOpened: ( value: boolean ) => void;
undo: () => void;
};
@@ -15,8 +17,10 @@ type EditorContextType = {
export const EditorContext = createContext< EditorContextType >( {
hasRedo: false,
hasUndo: false,
+ isDocumentOverviewOpened: false,
isInserterOpened: false,
redo: () => {},
+ setIsDocumentOverviewOpened: () => {},
setIsInserterOpened: () => {},
undo: () => {},
} );
diff --git a/packages/js/product-editor/src/components/iframe-editor/header-toolbar/document-overview/document-overview.tsx b/packages/js/product-editor/src/components/iframe-editor/header-toolbar/document-overview/document-overview.tsx
new file mode 100644
index 00000000000..7811333fbc2
--- /dev/null
+++ b/packages/js/product-editor/src/components/iframe-editor/header-toolbar/document-overview/document-overview.tsx
@@ -0,0 +1,41 @@
+/**
+ * External dependencies
+ */
+import { Button } from '@wordpress/components';
+import { createElement, forwardRef, useContext } from '@wordpress/element';
+import { listView as listViewIcon } from '@wordpress/icons';
+import { __ } from '@wordpress/i18n';
+import { Ref } from 'react';
+
+/**
+ * Internal dependencies
+ */
+import { DocumentOverviewProps } from './types';
+import { EditorContext } from '../../context';
+
+export const DocumentOverview = forwardRef(
+ function ForwardedRefDocumentOverview(
+ props: DocumentOverviewProps,
+ ref: Ref< HTMLButtonElement >
+ ) {
+ const { isDocumentOverviewOpened, setIsDocumentOverviewOpened } =
+ useContext( EditorContext );
+
+ function handleClick() {
+ setIsDocumentOverviewOpened( ! isDocumentOverviewOpened );
+ }
+
+ return (
+
+ );
+ }
+);
diff --git a/packages/js/product-editor/src/components/iframe-editor/header-toolbar/document-overview/index.ts b/packages/js/product-editor/src/components/iframe-editor/header-toolbar/document-overview/index.ts
new file mode 100644
index 00000000000..097d5af1a3f
--- /dev/null
+++ b/packages/js/product-editor/src/components/iframe-editor/header-toolbar/document-overview/index.ts
@@ -0,0 +1,2 @@
+export * from './document-overview';
+export * from './types';
diff --git a/packages/js/product-editor/src/components/iframe-editor/header-toolbar/document-overview/types.ts b/packages/js/product-editor/src/components/iframe-editor/header-toolbar/document-overview/types.ts
new file mode 100644
index 00000000000..ae379241c26
--- /dev/null
+++ b/packages/js/product-editor/src/components/iframe-editor/header-toolbar/document-overview/types.ts
@@ -0,0 +1 @@
+export type DocumentOverviewProps = { [ key: string ]: unknown };
diff --git a/packages/js/product-editor/src/components/iframe-editor/header-toolbar/header-toolbar.tsx b/packages/js/product-editor/src/components/iframe-editor/header-toolbar/header-toolbar.tsx
index 195d625b1f8..5b836fb1a29 100644
--- a/packages/js/product-editor/src/components/iframe-editor/header-toolbar/header-toolbar.tsx
+++ b/packages/js/product-editor/src/components/iframe-editor/header-toolbar/header-toolbar.tsx
@@ -31,6 +31,7 @@ import { Button, ToolbarItem } from '@wordpress/components';
import { EditorContext } from '../context';
import EditorHistoryRedo from './editor-history-redo';
import EditorHistoryUndo from './editor-history-undo';
+import { DocumentOverview } from './document-overview';
export function HeaderToolbar() {
const { isInserterOpened, setIsInserterOpened } =
@@ -114,6 +115,7 @@ export function HeaderToolbar() {
) }
+
>
) }
diff --git a/packages/js/product-editor/src/components/iframe-editor/iframe-editor.tsx b/packages/js/product-editor/src/components/iframe-editor/iframe-editor.tsx
index 61a137016a5..6ae18297ec2 100644
--- a/packages/js/product-editor/src/components/iframe-editor/iframe-editor.tsx
+++ b/packages/js/product-editor/src/components/iframe-editor/iframe-editor.tsx
@@ -53,6 +53,7 @@ export function IframeEditor( {
setBlocks,
} );
const [ isInserterOpened, setIsInserterOpened ] = useState( false );
+ const [ isListViewOpened, setIsListViewOpened ] = useState( false );
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This action exists in the block editor store.
const { clearSelectedBlock, updateSettings } =
@@ -80,8 +81,10 @@ export function IframeEditor( {
hasRedo,
hasUndo,
isInserterOpened,
+ isDocumentOverviewOpened: isListViewOpened,
redo,
setIsInserterOpened,
+ setIsDocumentOverviewOpened: setIsListViewOpened,
undo,
} }
>
diff --git a/packages/js/product-editor/src/components/iframe-editor/secondary-sidebar/document-overview-sidebar/document-overview-sidebar.tsx b/packages/js/product-editor/src/components/iframe-editor/secondary-sidebar/document-overview-sidebar/document-overview-sidebar.tsx
new file mode 100644
index 00000000000..c79f960a145
--- /dev/null
+++ b/packages/js/product-editor/src/components/iframe-editor/secondary-sidebar/document-overview-sidebar/document-overview-sidebar.tsx
@@ -0,0 +1,111 @@
+/**
+ * External dependencies
+ */
+import { Button, TabPanel } from '@wordpress/components';
+import {
+ useFocusOnMount,
+ useFocusReturn,
+ useMergeRefs,
+} from '@wordpress/compose';
+import {
+ createElement,
+ useRef,
+ useState,
+ useContext,
+} from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+import { closeSmall } from '@wordpress/icons';
+import {
+ // @ts-expect-error Module "@wordpress/block-editor" has no exported member '__experimentalListView'
+ __experimentalListView as ListView,
+} from '@wordpress/block-editor';
+
+/**
+ * Internal dependencies
+ */
+import { EditorContext } from '../../context';
+
+export function DocumentOverviewSidebar() {
+ const { setIsDocumentOverviewOpened: setIsListViewOpened } =
+ useContext( EditorContext );
+
+ // This hook handles focus when the sidebar first renders.
+ const focusOnMountRef = useFocusOnMount( 'firstElement' );
+ // The next 2 hooks handle focus for when the sidebar closes and returning focus to the element that had focus before sidebar opened.
+ const headerFocusReturnRef = useFocusReturn();
+ const contentFocusReturnRef = useFocusReturn();
+
+ function closeOnEscape( event: React.KeyboardEvent< HTMLDivElement > ) {
+ if ( event.code === 'Escape' && ! event.defaultPrevented ) {
+ event.preventDefault();
+ setIsListViewOpened( false );
+ }
+ }
+
+ // Use internal state instead of a ref to make sure that the component
+ // re-renders when the dropZoneElement updates.
+ const [ dropZoneElement, setDropZoneElement ] = useState( null );
+ // Tracks our current tab.
+ const [ tab, setTab ] = useState( 'list-view' );
+
+ // This ref refers to the list view application area.
+ const listViewRef = useRef< HTMLDivElement >( null );
+
+ // Must merge the refs together so focus can be handled properly in the next function.
+ const listViewContainerRef = useMergeRefs( [
+ contentFocusReturnRef,
+ focusOnMountRef,
+ listViewRef,
+ setDropZoneElement,
+ ] );
+
+ /**
+ * Render tab content for a given tab name.
+ *
+ * @param tabName The name of the tab to render.
+ */
+ function renderTabContent( tabName: string ) {
+ if ( tabName === 'list-view' ) {
+ return ;
+ }
+ return null;
+ }
+
+ return (
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
+
+
+ );
+}
diff --git a/packages/js/product-editor/src/components/iframe-editor/secondary-sidebar/document-overview-sidebar/index.ts b/packages/js/product-editor/src/components/iframe-editor/secondary-sidebar/document-overview-sidebar/index.ts
new file mode 100644
index 00000000000..d634a9b8387
--- /dev/null
+++ b/packages/js/product-editor/src/components/iframe-editor/secondary-sidebar/document-overview-sidebar/index.ts
@@ -0,0 +1 @@
+export * from './document-overview-sidebar';
diff --git a/packages/js/product-editor/src/components/iframe-editor/secondary-sidebar/document-overview-sidebar/style.scss b/packages/js/product-editor/src/components/iframe-editor/secondary-sidebar/document-overview-sidebar/style.scss
new file mode 100644
index 00000000000..0bec179b459
--- /dev/null
+++ b/packages/js/product-editor/src/components/iframe-editor/secondary-sidebar/document-overview-sidebar/style.scss
@@ -0,0 +1,39 @@
+.woocommerce-iframe-editor__document-overview-sidebar {
+ position: relative;
+ width: 350px;
+ height: 100%;
+ border-right: 1px solid $gray-400;
+
+ &-close-button {
+ position: absolute;
+ right: calc($grid-unit - 2px);
+ top: calc($grid-unit - 2px);
+ }
+
+ &-tab-panel {
+ height: 100%;
+
+ .components-tab-panel__tabs {
+ padding-right: $grid-unit-60;
+ border-bottom: 1px solid $gray-400;
+ }
+
+ .components-tab-panel__tab-content {
+ display: flex;
+ flex-direction: column;
+ height: calc(100% - $grid-unit-60);
+ }
+ }
+
+ &-tab-content {
+ height: 100%;
+ overflow-x: hidden;
+ overflow-y: auto;
+ padding: $grid-unit calc($grid-unit - 2px);
+
+ .block-editor-list-view-tree {
+ margin: 0;
+ width: 100%;
+ }
+ }
+}
diff --git a/packages/js/product-editor/src/components/iframe-editor/secondary-sidebar/secondary-sidebar.tsx b/packages/js/product-editor/src/components/iframe-editor/secondary-sidebar/secondary-sidebar.tsx
index 440c00a7100..712811c0961 100644
--- a/packages/js/product-editor/src/components/iframe-editor/secondary-sidebar/secondary-sidebar.tsx
+++ b/packages/js/product-editor/src/components/iframe-editor/secondary-sidebar/secondary-sidebar.tsx
@@ -8,13 +8,19 @@ import { createElement, useContext } from '@wordpress/element';
*/
import { EditorContext } from '../context';
import InserterSidebar from './inserter-sidebar';
+import { DocumentOverviewSidebar } from './document-overview-sidebar';
export function SecondarySidebar() {
- const { isInserterOpened } = useContext( EditorContext );
+ const { isInserterOpened, isDocumentOverviewOpened: isListViewOpened } =
+ useContext( EditorContext );
if ( isInserterOpened ) {
return ;
}
+ if ( isListViewOpened ) {
+ return ;
+ }
+
return null;
}
diff --git a/packages/js/product-editor/src/components/iframe-editor/style.scss b/packages/js/product-editor/src/components/iframe-editor/style.scss
index 95ae2fe3268..a344d5ca754 100644
--- a/packages/js/product-editor/src/components/iframe-editor/style.scss
+++ b/packages/js/product-editor/src/components/iframe-editor/style.scss
@@ -1,5 +1,6 @@
-@import './iframe-editor.scss';
-@import './header-toolbar/header-toolbar.scss';
-@import './resize-handle.scss';
-@import './back-button.scss';
-@import './secondary-sidebar/inserter-sidebar.scss';
+@import "./iframe-editor.scss";
+@import "./header-toolbar/header-toolbar.scss";
+@import "./resize-handle.scss";
+@import "./back-button.scss";
+@import "./secondary-sidebar/inserter-sidebar.scss";
+@import "./secondary-sidebar/document-overview-sidebar/style.scss";