You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ly...@apache.org on 2022/08/22 23:45:17 UTC

[superset] branch master updated: feat: Create dataset blank state (#21058)

This is an automated email from the ASF dual-hosted git repository.

lyndsi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 6094274f0b feat: Create dataset blank state (#21058)
6094274f0b is described below

commit 6094274f0b017b4a456cd78c3039a57be2d63fc5
Author: Lyndsi Kay Williams <55...@users.noreply.github.com>
AuthorDate: Mon Aug 22 18:45:03 2022 -0500

    feat: Create dataset blank state (#21058)
    
    * beginning of routing
    
    * elizabeth review
    
    * changed folder layout
    
    * Dataset layout setup
    
    * Fix DatasetLayout test
    
    * Fixed height issue
    
    * Fix link to sqllab
    
    * Fix DatasetPanel test
    
    * Remove arbitrary right panel
    
    * Add empty state SVGs
    
    * Restructure/rename some Dataset files
    
    * Fixed styling issue
    
    * Fix background color
    
    * Fix header border
    
    * Fix empty DatasetPanel styles
    
    * Adjust description rendering in DatasetPanel to fix tests
    
    * Add spaces back to blank dataset description to fix tests
    
    * Fix blank dataset description styles
    
    Co-authored-by: AAfghahi <ar...@gmail.com>
---
 .../src/assets/images/empty-dataset.svg            | 38 +++++++++
 .../src/assets/images/empty-table.svg              | 22 +++++
 .../src/components/EmptyState/index.tsx            |  4 +-
 .../components/ExploreChartHeader/index.jsx        |  4 +-
 .../index.tsx => AddDataset/AddDataset.test.tsx}   | 25 +++++-
 .../AddDataset/DatasetPanel/DatasetPanel.test.tsx  | 41 ++++++++++
 .../DatasetPanel/index.tsx                         | 34 +++++++-
 .../Footer/Footer.test.tsx}                        | 12 ++-
 .../{DatasetPage => AddDataset}/Footer/index.tsx   |  0
 .../Header/Header.test.tsx}                        | 12 ++-
 .../{DatasetPage => AddDataset}/Header/index.tsx   |  0
 .../LeftPanel/LeftPanel.test.tsx}                  | 14 +++-
 .../LeftPanel/index.tsx                            | 12 ++-
 .../RightPanel/RightPanel.test.tsx}                | 12 ++-
 .../RightPanel/index.tsx                           |  0
 .../dataset/{DatasetPage => AddDataset}/index.tsx  | 20 ++---
 .../dataset/{DatasetPage => AddDataset}/types.tsx  |  0
 .../dataset/DatasetLayout/DatasetLayout.test.tsx   | 70 ++++++++++++++++
 .../CRUD/data/dataset/DatasetLayout/index.tsx      | 72 ++++++++++++++++
 .../src/views/CRUD/data/dataset/styles.ts          | 95 ++++++++++++++++++++++
 superset-frontend/src/views/routes.tsx             |  8 +-
 21 files changed, 458 insertions(+), 37 deletions(-)

diff --git a/superset-frontend/src/assets/images/empty-dataset.svg b/superset-frontend/src/assets/images/empty-dataset.svg
new file mode 100644
index 0000000000..5ce1752545
--- /dev/null
+++ b/superset-frontend/src/assets/images/empty-dataset.svg
@@ -0,0 +1,38 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<svg width="152" height="152" viewBox="0 0 152 152" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M44.5 1C45.0523 1 45.5 1.44772 45.5 2V45C45.5 45.5523 45.0523 46 44.5 46H1.5C0.947716 46 0.5 45.5523 0.5 45V2C0.5 1.44772 0.947716 1 1.5 1L44.5 1Z" fill="white"/>
+<path d="M97.5 1C98.0523 1 98.5 1.44772 98.5 2V45C98.5 45.5523 98.0523 46 97.5 46H54.5C53.9477 46 53.5 45.5523 53.5 45V2C53.5 1.44772 53.9477 1 54.5 1L97.5 1Z" fill="white"/>
+<path d="M150.5 1C151.052 1 151.5 1.44772 151.5 2V45C151.5 45.5523 151.052 46 150.5 46H107.5C106.948 46 106.5 45.5523 106.5 45V2C106.5 1.44772 106.948 1 107.5 1L150.5 1Z" fill="white"/>
+<path d="M44.5 54C45.0523 54 45.5 54.4477 45.5 55V98C45.5 98.5523 45.0523 99 44.5 99H1.5C0.947716 99 0.5 98.5523 0.5 98V55C0.5 54.4477 0.947716 54 1.5 54H44.5Z" fill="white"/>
+<path d="M97.5 54C98.0523 54 98.5 54.4477 98.5 55V98C98.5 98.5523 98.0523 99 97.5 99H54.5C53.9477 99 53.5 98.5523 53.5 98V55C53.5 54.4477 53.9477 54 54.5 54H97.5Z" fill="white"/>
+<path d="M150.5 54C151.052 54 151.5 54.4477 151.5 55V98C151.5 98.5523 151.052 99 150.5 99H107.5C106.948 99 106.5 98.5523 106.5 98V55C106.5 54.4477 106.948 54 107.5 54H150.5Z" fill="white"/>
+<path d="M44.5 106C45.0523 106 45.5 106.448 45.5 107V150C45.5 150.552 45.0523 151 44.5 151H1.5C0.947716 151 0.5 150.552 0.5 150V107C0.5 106.448 0.947716 106 1.5 106H44.5Z" fill="white"/>
+<path d="M97.5 106C98.0523 106 98.5 106.448 98.5 107V150C98.5 150.552 98.0523 151 97.5 151H54.5C53.9477 151 53.5 150.552 53.5 150V107C53.5 106.448 53.9477 106 54.5 106H97.5Z" fill="white"/>
+<path d="M150.5 106C151.052 106 151.5 106.448 151.5 107V150C151.5 150.552 151.052 151 150.5 151H107.5C106.948 151 106.5 150.552 106.5 150V107C106.5 106.448 106.948 106 107.5 106H150.5Z" fill="white"/>
+<path d="M44.5 1C45.0523 1 45.5 1.44772 45.5 2V45C45.5 45.5523 45.0523 46 44.5 46H1.5C0.947716 46 0.5 45.5523 0.5 45V2C0.5 1.44772 0.947716 1 1.5 1L44.5 1Z" stroke="#E0E0E0"/>
+<path d="M97.5 1C98.0523 1 98.5 1.44772 98.5 2V45C98.5 45.5523 98.0523 46 97.5 46H54.5C53.9477 46 53.5 45.5523 53.5 45V2C53.5 1.44772 53.9477 1 54.5 1L97.5 1Z" stroke="#E0E0E0"/>
+<path d="M150.5 1C151.052 1 151.5 1.44772 151.5 2V45C151.5 45.5523 151.052 46 150.5 46H107.5C106.948 46 106.5 45.5523 106.5 45V2C106.5 1.44772 106.948 1 107.5 1L150.5 1Z" stroke="#E0E0E0"/>
+<path d="M44.5 54C45.0523 54 45.5 54.4477 45.5 55V98C45.5 98.5523 45.0523 99 44.5 99H1.5C0.947716 99 0.5 98.5523 0.5 98V55C0.5 54.4477 0.947716 54 1.5 54H44.5Z" stroke="#E0E0E0"/>
+<path d="M97.5 54C98.0523 54 98.5 54.4477 98.5 55V98C98.5 98.5523 98.0523 99 97.5 99H54.5C53.9477 99 53.5 98.5523 53.5 98V55C53.5 54.4477 53.9477 54 54.5 54H97.5Z" stroke="#E0E0E0"/>
+<path d="M150.5 54C151.052 54 151.5 54.4477 151.5 55V98C151.5 98.5523 151.052 99 150.5 99H107.5C106.948 99 106.5 98.5523 106.5 98V55C106.5 54.4477 106.948 54 107.5 54H150.5Z" stroke="#E0E0E0"/>
+<path d="M44.5 106C45.0523 106 45.5 106.448 45.5 107V150C45.5 150.552 45.0523 151 44.5 151H1.5C0.947716 151 0.5 150.552 0.5 150V107C0.5 106.448 0.947716 106 1.5 106H44.5Z" stroke="#E0E0E0"/>
+<path d="M97.5 106C98.0523 106 98.5 106.448 98.5 107V150C98.5 150.552 98.0523 151 97.5 151H54.5C53.9477 151 53.5 150.552 53.5 150V107C53.5 106.448 53.9477 106 54.5 106H97.5Z" stroke="#E0E0E0"/>
+<path d="M150.5 106C151.052 106 151.5 106.448 151.5 107V150C151.5 150.552 151.052 151 150.5 151H107.5C106.948 151 106.5 150.552 106.5 150V107C106.5 106.448 106.948 106 107.5 106H150.5Z" stroke="#E0E0E0"/>
+</svg>
diff --git a/superset-frontend/src/assets/images/empty-table.svg b/superset-frontend/src/assets/images/empty-table.svg
new file mode 100644
index 0000000000..c1062502f3
--- /dev/null
+++ b/superset-frontend/src/assets/images/empty-table.svg
@@ -0,0 +1,22 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<svg width="81" height="82" viewBox="0 0 81 82" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M76.5 1H4.5C2.29086 1 0.5 2.79086 0.5 5V77C0.5 79.2091 2.29086 81 4.5 81H76.5C78.7091 81 80.5 79.2091 80.5 77V5C80.5 2.79086 78.7091 1 76.5 1ZM36.5 73H8.5V57H36.5V73ZM36.5 49H8.5V33H36.5V49ZM72.5 73H44.5V57H72.5V73ZM72.5 49H44.5V33H72.5V49ZM72.5 25H8.5V9H72.5V25Z" fill="#F7F7F7"/>
+<path d="M36.5 73V73.5H37V73H36.5ZM8.5 73H8V73.5H8.5V73ZM8.5 57V56.5H8V57H8.5ZM36.5 57H37V56.5H36.5V57ZM36.5 49V49.5H37V49H36.5ZM8.5 49H8V49.5H8.5V49ZM8.5 33V32.5H8V33H8.5ZM36.5 33H37V32.5H36.5V33ZM72.5 73V73.5H73V73H72.5ZM44.5 73H44V73.5H44.5V73ZM44.5 57V56.5H44V57H44.5ZM72.5 57H73V56.5H72.5V57ZM72.5 49V49.5H73V49H72.5ZM44.5 49H44V49.5H44.5V49ZM44.5 33V32.5H44V33H44.5ZM72.5 33H73V32.5H72.5V33ZM72.5 25V25.5H73V25H72.5ZM8.5 25H8V25.5H8.5V25ZM8.5 9V8.5H8V9H8.5ZM72.5 9H73V8.5H72.5V9ZM76.5 0 [...]
+</svg>
diff --git a/superset-frontend/src/components/EmptyState/index.tsx b/superset-frontend/src/components/EmptyState/index.tsx
index 7ee69d7eea..3f5d586cf8 100644
--- a/superset-frontend/src/components/EmptyState/index.tsx
+++ b/superset-frontend/src/components/EmptyState/index.tsx
@@ -37,6 +37,7 @@ export interface EmptyStateSmallProps {
 export interface EmptyStateProps extends EmptyStateSmallProps {
   buttonText?: ReactNode;
   buttonAction?: React.MouseEventHandler<HTMLElement>;
+  className?: string;
 }
 
 export interface ImageContainerProps {
@@ -152,8 +153,9 @@ export const EmptyStateBig = ({
   description,
   buttonAction,
   buttonText,
+  className,
 }: EmptyStateProps) => (
-  <EmptyStateContainer>
+  <EmptyStateContainer className={className}>
     <ImageContainer image={image} size={EmptyStateSize.Big} />
     <TextContainer
       css={(theme: SupersetTheme) =>
diff --git a/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx b/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx
index fb85882f02..9e1b27b24d 100644
--- a/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx
+++ b/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx
@@ -119,9 +119,7 @@ export const ExploreChartHeader = ({
   };
 
   useEffect(() => {
-    if (dashboardId) {
-      fetchChartDashboardData();
-    }
+    if (dashboardId) fetchChartDashboardData();
   }, []);
 
   const openPropertiesModal = () => {
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Header/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/AddDataset.test.tsx
similarity index 51%
copy from superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Header/index.tsx
copy to superset-frontend/src/views/CRUD/data/dataset/AddDataset/AddDataset.test.tsx
index 44f0e19f7b..23c5c3b471 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Header/index.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/AddDataset.test.tsx
@@ -17,7 +17,26 @@
  * under the License.
  */
 import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import AddDataset from 'src/views/CRUD/data/dataset/AddDataset';
 
-export default function Header() {
-  return <div>Header</div>;
-}
+describe('AddDataset', () => {
+  it('renders a blank state AddDataset', () => {
+    render(<AddDataset />);
+
+    const blankeStateImgs = screen.getAllByRole('img', { name: /empty/i });
+
+    // Header
+    expect(screen.getByText(/header/i)).toBeVisible();
+    // Left panel
+    expect(blankeStateImgs[0]).toBeVisible();
+    expect(screen.getByText(/no database tables found/i)).toBeVisible();
+    // Database panel
+    expect(blankeStateImgs[1]).toBeVisible();
+    expect(screen.getByText(/select dataset source/i)).toBeVisible();
+    // Footer
+    expect(screen.getByText(/footer/i)).toBeVisible();
+
+    expect(blankeStateImgs.length).toBe(2);
+  });
+});
diff --git a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/DatasetPanel/DatasetPanel.test.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/DatasetPanel/DatasetPanel.test.tsx
new file mode 100644
index 0000000000..b03b7cad92
--- /dev/null
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/DatasetPanel/DatasetPanel.test.tsx
@@ -0,0 +1,41 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import DatasetPanel from 'src/views/CRUD/data/dataset/AddDataset/DatasetPanel';
+
+describe('DatasetPanel', () => {
+  it('renders a blank state DatasetPanel', () => {
+    render(<DatasetPanel />);
+
+    const blankDatasetImg = screen.getByRole('img', { name: /empty/i });
+    const blankDatasetTitle = screen.getByText(/select dataset source/i);
+    const blankDatasetDescription = screen.getByText(
+      /datasets can be created from database tables or sql queries\. select a database table to the left or to open sql lab\. from there you can save the query as a dataset\./i,
+    );
+    const sqlLabLink = screen.getByRole('button', {
+      name: /create dataset from sql query/i,
+    });
+
+    expect(blankDatasetImg).toBeVisible();
+    expect(blankDatasetTitle).toBeVisible();
+    expect(blankDatasetDescription).toBeVisible();
+    expect(sqlLabLink).toBeVisible();
+  });
+});
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/DatasetPanel/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/DatasetPanel/index.tsx
similarity index 51%
copy from superset-frontend/src/views/CRUD/data/dataset/DatasetPage/DatasetPanel/index.tsx
copy to superset-frontend/src/views/CRUD/data/dataset/AddDataset/DatasetPanel/index.tsx
index 9fe93b8fb5..294637a6f7 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/DatasetPanel/index.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/DatasetPanel/index.tsx
@@ -17,7 +17,39 @@
  * under the License.
  */
 import React from 'react';
+import { t, styled } from '@superset-ui/core';
+import { EmptyStateBig } from 'src/components/EmptyState';
+
+const StyledEmptyStateBig = styled(EmptyStateBig)`
+  p {
+    width: ${({ theme }) => theme.gridUnit * 115}px;
+  }
+`;
+
+const renderDescription = () => (
+  <>
+    {t(
+      'Datasets can be created from database tables or SQL queries. Select a database table to the left or ',
+    )}
+    <span
+      role="button"
+      onClick={() => {
+        window.location.href = `/superset/sqllab`;
+      }}
+      tabIndex={0}
+    >
+      {t('create dataset from SQL query')}
+    </span>
+    {t(' to open SQL Lab. From there you can save the query as a dataset.')}
+  </>
+);
 
 export default function DatasetPanel() {
-  return <div>Dataset Panel</div>;
+  return (
+    <StyledEmptyStateBig
+      image="empty-dataset.svg"
+      title={t('Select dataset source')}
+      description={renderDescription()}
+    />
+  );
 }
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/DatasetPanel/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/Footer/Footer.test.tsx
similarity index 74%
rename from superset-frontend/src/views/CRUD/data/dataset/DatasetPage/DatasetPanel/index.tsx
rename to superset-frontend/src/views/CRUD/data/dataset/AddDataset/Footer/Footer.test.tsx
index 9fe93b8fb5..bbff68c88d 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/DatasetPanel/index.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/Footer/Footer.test.tsx
@@ -17,7 +17,13 @@
  * under the License.
  */
 import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import Footer from 'src/views/CRUD/data/dataset/AddDataset/Footer';
 
-export default function DatasetPanel() {
-  return <div>Dataset Panel</div>;
-}
+describe('Footer', () => {
+  it('renders a blank state Footer', () => {
+    render(<Footer />);
+
+    expect(screen.getByText(/footer/i)).toBeVisible();
+  });
+});
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Footer/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/Footer/index.tsx
similarity index 100%
rename from superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Footer/index.tsx
rename to superset-frontend/src/views/CRUD/data/dataset/AddDataset/Footer/index.tsx
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Header/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/Header/Header.test.tsx
similarity index 74%
copy from superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Header/index.tsx
copy to superset-frontend/src/views/CRUD/data/dataset/AddDataset/Header/Header.test.tsx
index 44f0e19f7b..c913dbc86a 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Header/index.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/Header/Header.test.tsx
@@ -17,7 +17,13 @@
  * under the License.
  */
 import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import Header from 'src/views/CRUD/data/dataset/AddDataset/Header';
 
-export default function Header() {
-  return <div>Header</div>;
-}
+describe('Header', () => {
+  it('renders a blank state Header', () => {
+    render(<Header />);
+
+    expect(screen.getByText(/header/i)).toBeVisible();
+  });
+});
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Header/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/Header/index.tsx
similarity index 100%
copy from superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Header/index.tsx
copy to superset-frontend/src/views/CRUD/data/dataset/AddDataset/Header/index.tsx
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Header/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftPanel/LeftPanel.test.tsx
similarity index 63%
copy from superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Header/index.tsx
copy to superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftPanel/LeftPanel.test.tsx
index 44f0e19f7b..9fdb1aee8f 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Header/index.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftPanel/LeftPanel.test.tsx
@@ -17,7 +17,15 @@
  * under the License.
  */
 import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import LeftPanel from 'src/views/CRUD/data/dataset/AddDataset/LeftPanel';
 
-export default function Header() {
-  return <div>Header</div>;
-}
+describe('LeftPanel', () => {
+  it('renders a blank state LeftPanel', () => {
+    render(<LeftPanel />);
+
+    expect(screen.getByRole('img', { name: /empty/i })).toBeVisible();
+    expect(screen.getByText(/no database tables found/i)).toBeVisible();
+    expect(screen.getByText(/try selecting a different schema/i)).toBeVisible();
+  });
+});
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/LeftPanel/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftPanel/index.tsx
similarity index 74%
rename from superset-frontend/src/views/CRUD/data/dataset/DatasetPage/LeftPanel/index.tsx
rename to superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftPanel/index.tsx
index 5ffb6a12c9..908cf1a833 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/LeftPanel/index.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftPanel/index.tsx
@@ -17,7 +17,17 @@
  * under the License.
  */
 import React from 'react';
+import { t } from '@superset-ui/core';
+import { EmptyStateMedium } from 'src/components/EmptyState';
 
 export default function LeftPanel() {
-  return <div>Left Panel</div>;
+  return (
+    <>
+      <EmptyStateMedium
+        image="empty-table.svg"
+        title={t('No database tables found')}
+        description={t('Try selecting a different schema')}
+      />
+    </>
+  );
 }
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Header/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/RightPanel/RightPanel.test.tsx
similarity index 72%
rename from superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Header/index.tsx
rename to superset-frontend/src/views/CRUD/data/dataset/AddDataset/RightPanel/RightPanel.test.tsx
index 44f0e19f7b..987d96abfa 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/Header/index.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/RightPanel/RightPanel.test.tsx
@@ -17,7 +17,13 @@
  * under the License.
  */
 import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import RightPanel from 'src/views/CRUD/data/dataset/AddDataset/RightPanel';
 
-export default function Header() {
-  return <div>Header</div>;
-}
+describe('RightPanel', () => {
+  it('renders a blank state RightPanel', () => {
+    render(<RightPanel />);
+
+    expect(screen.getByText(/right panel/i)).toBeVisible();
+  });
+});
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/RightPanel/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/RightPanel/index.tsx
similarity index 100%
rename from superset-frontend/src/views/CRUD/data/dataset/DatasetPage/RightPanel/index.tsx
rename to superset-frontend/src/views/CRUD/data/dataset/AddDataset/RightPanel/index.tsx
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/index.tsx
similarity index 87%
rename from superset-frontend/src/views/CRUD/data/dataset/DatasetPage/index.tsx
rename to superset-frontend/src/views/CRUD/data/dataset/AddDataset/index.tsx
index 42de041fa0..677f3f52ae 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/index.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/index.tsx
@@ -17,13 +17,12 @@
  * under the License.
  */
 import React from 'react';
-// import React, { useReducer, Reducer } from 'react';
 import Header from './Header';
 import DatasetPanel from './DatasetPanel';
 import LeftPanel from './LeftPanel';
-import RightPanel from './RightPanel';
 import Footer from './Footer';
 import { DatasetActionType, DatasetObject, DSReducerActionType } from './types';
+import DatasetLayout from '../DatasetLayout';
 
 export function datasetReducer(
   state: Partial<DatasetObject> | null,
@@ -61,7 +60,7 @@ export function datasetReducer(
   }
 }
 
-export default function DatasetPage() {
+export default function AddDataset() {
   // this is commented out for now, but can be commented in as the component
   // is built up. Uncomment the useReducer in imports too
   // const [dataset, setDataset] = useReducer<
@@ -69,14 +68,11 @@ export default function DatasetPage() {
   // >(datasetReducer, null);
 
   return (
-    <div>
-      <Header />
-      <LeftPanel />
-      <div css={{ display: 'flex' }}>
-        <DatasetPanel />
-        <Footer />
-      </div>
-      <RightPanel />
-    </div>
+    <DatasetLayout
+      header={Header()}
+      leftPanel={LeftPanel()}
+      datasetPanel={DatasetPanel()}
+      footer={Footer()}
+    />
   );
 }
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetPage/types.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/types.tsx
similarity index 100%
rename from superset-frontend/src/views/CRUD/data/dataset/DatasetPage/types.tsx
rename to superset-frontend/src/views/CRUD/data/dataset/AddDataset/types.tsx
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetLayout/DatasetLayout.test.tsx b/superset-frontend/src/views/CRUD/data/dataset/DatasetLayout/DatasetLayout.test.tsx
new file mode 100644
index 0000000000..59c3ee3ed1
--- /dev/null
+++ b/superset-frontend/src/views/CRUD/data/dataset/DatasetLayout/DatasetLayout.test.tsx
@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import DatasetLayout from 'src/views/CRUD/data/dataset/DatasetLayout';
+import Header from 'src/views/CRUD/data/dataset/AddDataset/Header';
+import LeftPanel from 'src/views/CRUD/data/dataset/AddDataset/LeftPanel';
+import DatasetPanel from 'src/views/CRUD/data/dataset/AddDataset/DatasetPanel';
+import RightPanel from 'src/views/CRUD/data/dataset/AddDataset/RightPanel';
+import Footer from 'src/views/CRUD/data/dataset/AddDataset/Footer';
+
+describe('DatasetLayout', () => {
+  it('renders nothing when no components are passed in', () => {
+    render(<DatasetLayout />);
+    const layoutWrapper = screen.getByTestId('dataset-layout-wrapper');
+
+    expect(layoutWrapper).toHaveTextContent('');
+  });
+
+  it('renders a Header when passed in', () => {
+    render(<DatasetLayout header={Header()} />);
+
+    expect(screen.getByText(/header/i)).toBeVisible();
+  });
+
+  it('renders a LeftPanel when passed in', () => {
+    render(<DatasetLayout leftPanel={LeftPanel()} />);
+
+    expect(screen.getByRole('img', { name: /empty/i })).toBeVisible();
+    expect(screen.getByText(/no database tables found/i)).toBeVisible();
+  });
+
+  it('renders a DatasetPanel when passed in', () => {
+    render(<DatasetLayout datasetPanel={DatasetPanel()} />);
+
+    const blankDatasetImg = screen.getByRole('img', { name: /empty/i });
+    const blankDatasetTitle = screen.getByText(/select dataset source/i);
+
+    expect(blankDatasetImg).toBeVisible();
+    expect(blankDatasetTitle).toBeVisible();
+  });
+
+  it('renders a RightPanel when passed in', () => {
+    render(<DatasetLayout rightPanel={RightPanel()} />);
+
+    expect(screen.getByText(/right panel/i)).toBeVisible();
+  });
+
+  it('renders a Footer when passed in', () => {
+    render(<DatasetLayout footer={Footer()} />);
+
+    expect(screen.getByText(/footer/i)).toBeVisible();
+  });
+});
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetLayout/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/DatasetLayout/index.tsx
new file mode 100644
index 0000000000..8587b25a4a
--- /dev/null
+++ b/superset-frontend/src/views/CRUD/data/dataset/DatasetLayout/index.tsx
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React, { ReactElement, JSXElementConstructor } from 'react';
+import {
+  StyledLayoutWrapper,
+  LeftColumn,
+  RightColumn,
+  OuterRow,
+  PanelRow,
+  FooterRow,
+  StyledHeader,
+  StyledLeftPanel,
+  StyledDatasetPanel,
+  StyledRightPanel,
+  StyledFooter,
+} from 'src/views/CRUD/data/dataset/styles';
+
+interface DatasetLayoutProps {
+  header?: ReactElement<any, string | JSXElementConstructor<any>> | null;
+  leftPanel?: ReactElement<any, string | JSXElementConstructor<any>> | null;
+  datasetPanel?: ReactElement<any, string | JSXElementConstructor<any>> | null;
+  rightPanel?: ReactElement<any, string | JSXElementConstructor<any>> | null;
+  footer?: ReactElement<any, string | JSXElementConstructor<any>> | null;
+}
+
+export default function DatasetLayout({
+  header,
+  leftPanel,
+  datasetPanel,
+  rightPanel,
+  footer,
+}: DatasetLayoutProps) {
+  return (
+    <StyledLayoutWrapper data-test="dataset-layout-wrapper">
+      {header && <StyledHeader>{header}</StyledHeader>}
+      <OuterRow>
+        <LeftColumn>
+          {leftPanel && <StyledLeftPanel>{leftPanel}</StyledLeftPanel>}
+        </LeftColumn>
+
+        <RightColumn>
+          <PanelRow>
+            {datasetPanel && (
+              <StyledDatasetPanel>{datasetPanel}</StyledDatasetPanel>
+            )}
+            {rightPanel && <StyledRightPanel>{rightPanel}</StyledRightPanel>}
+          </PanelRow>
+
+          <FooterRow>
+            {footer && <StyledFooter>{footer}</StyledFooter>}
+          </FooterRow>
+        </RightColumn>
+      </OuterRow>
+    </StyledLayoutWrapper>
+  );
+}
diff --git a/superset-frontend/src/views/CRUD/data/dataset/styles.ts b/superset-frontend/src/views/CRUD/data/dataset/styles.ts
new file mode 100644
index 0000000000..8d9f771dce
--- /dev/null
+++ b/superset-frontend/src/views/CRUD/data/dataset/styles.ts
@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { styled } from '@superset-ui/core';
+
+export const StyledLayoutWrapper = styled.div`
+  flex-grow: 1;
+  display: flex;
+  flex-direction: column;
+  background-color: ${({ theme }) => theme.colors.grayscale.light5};
+`;
+
+const Column = styled.div`
+  width: 100%;
+  height: 100%;
+  flex-direction: column;
+`;
+
+export const LeftColumn = styled(Column)`
+  width: ${({ theme }) => theme.gridUnit * 80}px;
+  height: auto;
+`;
+
+export const RightColumn = styled(Column)`
+  height: auto;
+  display: flex;
+  flex: 1 0 auto;
+  width: auto;
+`;
+
+const Row = styled.div`
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: row;
+`;
+
+export const OuterRow = styled(Row)`
+  flex: 1 0 auto;
+`;
+
+export const PanelRow = styled(Row)`
+  flex: 1 0 auto;
+  height: auto;
+`;
+
+export const FooterRow = styled(Row)`
+  flex: 0 0 auto;
+  height: ${({ theme }) => theme.gridUnit * 16}px;
+`;
+
+export const StyledHeader = styled.div`
+  flex: 0 0 auto;
+  height: ${({ theme }) => theme.gridUnit * 16}px;
+  border-bottom: 2px solid ${({ theme }) => theme.colors.grayscale.light2};
+  color: ${({ theme }) => theme.colors.error.base};
+`;
+
+export const StyledLeftPanel = styled.div`
+  width: ${({ theme }) => theme.gridUnit * 80}px;
+  height: 100%;
+  border-right: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
+`;
+
+export const StyledDatasetPanel = styled.div`
+  width: 100%;
+`;
+
+export const StyledRightPanel = styled.div`
+  border-left: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
+  color: ${({ theme }) => theme.colors.success.base};
+`;
+
+export const StyledFooter = styled.div`
+  height: ${({ theme }) => theme.gridUnit * 16}px;
+  width: 100%;
+  border-top: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
+  border-bottom: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
+  color: ${({ theme }) => theme.colors.info.base};
+`;
diff --git a/superset-frontend/src/views/routes.tsx b/superset-frontend/src/views/routes.tsx
index 732686838f..d90feec116 100644
--- a/superset-frontend/src/views/routes.tsx
+++ b/superset-frontend/src/views/routes.tsx
@@ -82,10 +82,10 @@ const DatasetList = lazy(
     ),
 );
 
-const DatasetPage = lazy(
+const AddDataset = lazy(
   () =>
     import(
-      /* webpackChunkName: "DatasetEditor" */ 'src/views/CRUD/data/dataset/DatasetPage/index'
+      /* webpackChunkName: "DatasetEditor" */ 'src/views/CRUD/data/dataset/AddDataset/index'
     ),
 );
 
@@ -199,11 +199,11 @@ export const routes: Routes = [
   },
   {
     path: '/dataset/add/',
-    Component: DatasetPage,
+    Component: AddDataset,
   },
   {
     path: '/dataset/:datasetId',
-    Component: DatasetPage,
+    Component: AddDataset,
   },
 ];