diff --git a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/AddDataset.test.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/AddDataset.test.tsx
index 7e5a7de12ab3a..ea595c0f901a0 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/AddDataset.test.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/AddDataset.test.tsx
@@ -26,6 +26,7 @@ jest.mock('react-router-dom', () => ({
useHistory: () => ({
push: mockHistoryPush,
}),
+ useParams: () => ({ datasetId: undefined }),
}));
describe('AddDataset', () => {
diff --git a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/EditDataset/EditDataset.test.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/EditDataset/EditDataset.test.tsx
new file mode 100644
index 0000000000000..c24496facc871
--- /dev/null
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/EditDataset/EditDataset.test.tsx
@@ -0,0 +1,43 @@
+/**
+ * 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 fetchMock from 'fetch-mock';
+import { render, screen } from 'spec/helpers/testing-library';
+import EditDataset from './index';
+
+const DATASET_ENDPOINT = 'glob:*api/v1/dataset/1/related_objects';
+
+const mockedProps = {
+ id: '1',
+};
+
+fetchMock.get(DATASET_ENDPOINT, { charts: { results: [], count: 2 } });
+
+test('should render edit dataset view with tabs', async () => {
+ render();
+
+ const columnTab = await screen.findByRole('tab', { name: /columns/i });
+ const metricsTab = screen.getByRole('tab', { name: /metrics/i });
+ const usageTab = screen.getByRole('tab', { name: /usage/i });
+
+ expect(fetchMock.calls(DATASET_ENDPOINT)).toBeTruthy();
+ expect(columnTab).toBeInTheDocument();
+ expect(metricsTab).toBeInTheDocument();
+ expect(usageTab).toBeInTheDocument();
+});
diff --git a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/EditDataset/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/EditDataset/index.tsx
new file mode 100644
index 0000000000000..7abc676fcab5b
--- /dev/null
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/EditDataset/index.tsx
@@ -0,0 +1,74 @@
+/**
+ * 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, t } from '@superset-ui/core';
+import React from 'react';
+import { useGetDatasetRelatedCounts } from 'src/views/CRUD/data/hooks';
+import Badge from 'src/components/Badge';
+import Tabs from 'src/components/Tabs';
+
+const StyledTabs = styled(Tabs)`
+ ${({ theme }) => `
+ margin-top: ${theme.gridUnit * 8.5}px;
+ padding-left: ${theme.gridUnit * 4}px;
+
+ .ant-tabs-top > .ant-tabs-nav::before {
+ width: ${theme.gridUnit * 50}px;
+ }
+ `}
+`;
+
+const TabStyles = styled.div`
+ ${({ theme }) => `
+ .ant-badge {
+ width: ${theme.gridUnit * 8}px;
+ margin-left: ${theme.gridUnit * 2.5}px;
+ }
+ `}
+`;
+
+interface EditPageProps {
+ id: string;
+}
+
+const TRANSLATIONS = {
+ USAGE_TEXT: t('Usage'),
+ COLUMNS_TEXT: t('Columns'),
+ METRICS_TEXT: t('Metrics'),
+};
+
+const EditPage = ({ id }: EditPageProps) => {
+ const { usageCount } = useGetDatasetRelatedCounts(id);
+
+ const usageTab = (
+
+ {TRANSLATIONS.USAGE_TEXT}
+ {usageCount > 0 && }
+
+ );
+
+ return (
+
+
+
+
+
+ );
+};
+
+export default EditPage;
diff --git a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftPanel/LeftPanel.test.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftPanel/LeftPanel.test.tsx
index d44a441923935..3996dc0fec5c6 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftPanel/LeftPanel.test.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftPanel/LeftPanel.test.tsx
@@ -137,7 +137,7 @@ fetchMock.get(schemasEndpoint, {
});
fetchMock.get(tablesEndpoint, {
- tableLength: 3,
+ count: 3,
result: [
{ value: 'Sheet1', type: 'table', extra: null },
{ value: 'Sheet2', type: 'table', extra: null },
diff --git a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/index.tsx
index a6d84ff10ee60..67b108ab366f1 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/index.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/index.tsx
@@ -16,17 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-import React, {
- useReducer,
- Reducer,
- useEffect,
- useState,
- useCallback,
-} from 'react';
-import { logging, t } from '@superset-ui/core';
-import { UseGetDatasetsList } from 'src/views/CRUD/data/hooks';
-import { addDangerToast } from 'src/components/MessageToasts/actions';
+import React, { useReducer, Reducer, useEffect, useState } from 'react';
+import { useParams } from 'react-router-dom';
+import { useDatasetsList } from 'src/views/CRUD/data/hooks';
import Header from './Header';
+import EditPage from './EditDataset';
import DatasetPanel from './DatasetPanel';
import LeftPanel from './LeftPanel';
import Footer from './Footer';
@@ -82,35 +76,19 @@ export default function AddDataset() {
Reducer | null, DSReducerActionType>
>(datasetReducer, null);
const [hasColumns, setHasColumns] = useState(false);
- const [datasets, setDatasets] = useState([]);
- const datasetNames = datasets.map(dataset => dataset.table_name);
- const encodedSchema = dataset?.schema
- ? encodeURIComponent(dataset?.schema)
- : undefined;
+ const [editPageIsVisible, setEditPageIsVisible] = useState(false);
- const getDatasetsList = useCallback(async () => {
- if (dataset?.schema) {
- const filters = [
- { col: 'database', opr: 'rel_o_m', value: dataset?.db?.id },
- { col: 'schema', opr: 'eq', value: encodedSchema },
- { col: 'sql', opr: 'dataset_is_null_or_empty', value: true },
- ];
- await UseGetDatasetsList(filters)
- .then(results => {
- setDatasets(results);
- })
- .catch(error => {
- addDangerToast(t('There was an error fetching dataset'));
- logging.error(t('There was an error fetching dataset'), error);
- });
- }
- }, [dataset?.db?.id, dataset?.schema, encodedSchema]);
+ const { datasets, datasetNames } = useDatasetsList(
+ dataset?.db,
+ dataset?.schema,
+ );
+ const { datasetId: id } = useParams<{ datasetId: string }>();
useEffect(() => {
- if (dataset?.schema) {
- getDatasetsList();
+ if (!Number.isNaN(parseInt(id, 10))) {
+ setEditPageIsVisible(true);
}
- }, [dataset?.schema, getDatasetsList]);
+ }, [id]);
const HeaderComponent = () => (
@@ -124,6 +102,8 @@ export default function AddDataset() {
/>
);
+ const EditPageComponent = () => ;
+
const DatasetPanelComponent = () => (
);
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetLayout/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/DatasetLayout/index.tsx
index b5691fe5cb1d8..7702efcb59075 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/DatasetLayout/index.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/DatasetLayout/index.tsx
@@ -50,12 +50,11 @@ export default function DatasetLayout({
{header && {header}}
-
- {leftPanel && (
+ {leftPanel && (
+
{leftPanel}
- )}
-
-
+
+ )}
{datasetPanel && (
diff --git a/superset-frontend/src/views/CRUD/data/hooks.ts b/superset-frontend/src/views/CRUD/data/hooks.ts
index 0c1528f07f437..4e7a51de35213 100644
--- a/superset-frontend/src/views/CRUD/data/hooks.ts
+++ b/superset-frontend/src/views/CRUD/data/hooks.ts
@@ -16,15 +16,17 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { useState, useEffect } from 'react';
+import { useState, useEffect, useCallback } from 'react';
import { SupersetClient, logging, t } from '@superset-ui/core';
+import rison from 'rison';
import { addDangerToast } from 'src/components/MessageToasts/actions';
import { DatasetObject } from 'src/views/CRUD/data/dataset/AddDataset/types';
-import rison from 'rison';
+import { DatabaseObject } from 'src/components/DatabaseSelector';
type BaseQueryObject = {
id: number;
};
+
export function useQueryPreviewState({
queries,
fetchData,
@@ -81,35 +83,96 @@ export function useQueryPreviewState({
/**
* Retrieves all pages of dataset results
*/
-export const UseGetDatasetsList = async (filters: object[]) => {
- let results: DatasetObject[] = [];
- let page = 0;
- let count;
-
- // If count is undefined or less than results, we need to
- // asynchronously retrieve a page of dataset results
- while (count === undefined || results.length < count) {
- const queryParams = rison.encode_uri({ filters, page });
- try {
- // eslint-disable-next-line no-await-in-loop
- const response = await SupersetClient.get({
- endpoint: `/api/v1/dataset/?q=${queryParams}`,
- });
-
- // Reassign local count to response's count
- ({ count } = response.json);
-
- const {
- json: { result },
- } = response;
-
- results = [...results, ...result];
-
- page += 1;
- } catch (error) {
- addDangerToast(t('There was an error fetching dataset'));
- logging.error(t('There was an error fetching dataset'), error);
+export const useDatasetsList = (
+ db:
+ | (DatabaseObject & {
+ owners: [number];
+ })
+ | undefined,
+ schema: string | null | undefined,
+) => {
+ const [datasets, setDatasets] = useState([]);
+ const encodedSchema = schema ? encodeURIComponent(schema) : undefined;
+
+ const getDatasetsList = useCallback(async (filters: object[]) => {
+ let results: DatasetObject[] = [];
+ let page = 0;
+ let count;
+
+ // If count is undefined or less than results, we need to
+ // asynchronously retrieve a page of dataset results
+ while (count === undefined || results.length < count) {
+ const queryParams = rison.encode_uri({ filters, page });
+ try {
+ // eslint-disable-next-line no-await-in-loop
+ const response = await SupersetClient.get({
+ endpoint: `/api/v1/dataset/?q=${queryParams}`,
+ });
+
+ // Reassign local count to response's count
+ ({ count } = response.json);
+
+ const {
+ json: { result },
+ } = response;
+
+ results = [...results, ...result];
+
+ page += 1;
+ } catch (error) {
+ addDangerToast(t('There was an error fetching dataset'));
+ logging.error(t('There was an error fetching dataset'), error);
+ }
}
- }
- return results;
+
+ setDatasets(results);
+ }, []);
+
+ useEffect(() => {
+ const filters = [
+ { col: 'database', opr: 'rel_o_m', value: db?.id },
+ { col: 'schema', opr: 'eq', value: encodedSchema },
+ { col: 'sql', opr: 'dataset_is_null_or_empty', value: true },
+ ];
+
+ if (schema) {
+ getDatasetsList(filters);
+ }
+ }, [db?.id, schema, encodedSchema, getDatasetsList]);
+
+ const datasetNames = datasets?.map(dataset => dataset.table_name);
+
+ return { datasets, datasetNames };
+};
+
+export const useGetDatasetRelatedCounts = (id: string) => {
+ const [usageCount, setUsageCount] = useState(0);
+
+ const getDatasetRelatedObjects = useCallback(
+ () =>
+ SupersetClient.get({
+ endpoint: `/api/v1/dataset/${id}/related_objects`,
+ })
+ .then(({ json }) => {
+ setUsageCount(json?.charts.count);
+ })
+ .catch(error => {
+ addDangerToast(
+ t(`There was an error fetching dataset's related objects`),
+ );
+ logging.error(error);
+ }),
+ [id],
+ );
+
+ useEffect(() => {
+ // Todo: this useEffect should be used to call all count methods conncurently
+ // when we populate data for the new tabs. For right separating out this
+ // api call for building the usage page.
+ if (id) {
+ getDatasetRelatedObjects();
+ }
+ }, [id, getDatasetRelatedObjects]);
+
+ return { usageCount };
};