diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/sample-data-colored.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/sample-data-colored.svg new file mode 100644 index 00000000000..efd4c0dec0b --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/sample-data-colored.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/sample-data.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/sample-data.svg new file mode 100644 index 00000000000..fa1361da3a9 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/sample-data.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx index 8756b213c27..fe1232596db 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx @@ -40,6 +40,9 @@ import PageContainer from '../containers/PageContainer'; import Entitylineage from '../EntityLineage/EntityLineage.component'; import FrequentlyJoinedTables from '../FrequentlyJoinedTables/FrequentlyJoinedTables.component'; import ManageTab from '../ManageTab/ManageTab.component'; +import SampleDataTable, { + SampleColumns, +} from '../SampleDataTable/SampleDataTable.component'; import SchemaEditor from '../schema-editor/SchemaEditor'; import SchemaTab from '../SchemaTab/SchemaTab.component'; import TableProfiler from '../TableProfiler/TableProfiler.component'; @@ -164,6 +167,17 @@ const DatasetDetails: React.FC = ({ isHidden: !dataModel?.sql, position: 4, }, + { + name: 'Sample Data', + icon: { + alt: 'sample_data', + name: 'sample-data', + title: 'Sample Data', + selectedName: 'sample-data-color', + }, + isProtected: false, + position: 5, + }, { name: 'Manage', icon: { @@ -174,7 +188,7 @@ const DatasetDetails: React.FC = ({ }, isProtected: true, protectedState: !owner || hasEditAccess(), - position: 5, + position: 6, }, ]; @@ -331,6 +345,29 @@ const DatasetDetails: React.FC = ({ } }; + const getSampleDataWithType = () => { + const updatedColumns = sampleData?.columns?.map((column) => { + const matchedColumn = columns.find((col) => col.name === column); + + if (matchedColumn) { + return { + name: matchedColumn.name, + dataType: matchedColumn.dataType, + }; + } else { + return { + name: column, + dataType: '', + }; + } + }); + + return { + columns: updatedColumns as SampleColumns[] | undefined, + rows: sampleData?.rows, + }; + }; + useEffect(() => { if (isAuthDisabled && users.length && followers.length) { setFollowersData(followers); @@ -450,6 +487,11 @@ const DatasetDetails: React.FC = ({ )} {activeTab === 5 && ( +
+ +
+ )} + {activeTab === 6 && (
= ({ sampleData }: Props) => { + const tableRef = useRef(null); + const [scrollOffset, setScrollOffSet] = useState(0); + const [containerWidth, setContainerWidth] = useState(0); + const [scrollHandle, setScrollHandle] = useState<{ + left: boolean; + right: boolean; + }>({ left: true, right: true }); + + const scrollHandler = (scrollOffset: number) => { + if (tableRef.current) { + tableRef.current.scrollLeft += scrollOffset; + setScrollOffSet(tableRef.current.scrollLeft); + } + }; + + useLayoutEffect(() => { + setContainerWidth( + (tableRef.current?.scrollWidth ?? 0) - + (tableRef.current?.clientWidth ?? 0) + ); + }, []); + + useEffect(() => { + const rFlag = scrollOffset !== containerWidth; + const lFlag = scrollOffset > 0; + setScrollHandle((pre) => ({ ...pre, right: rFlag, left: lFlag })); + }, [scrollOffset, containerWidth]); + return ( -
- {sampleData?.rows?.length && sampleData?.columns?.length ? ( - - - - {sampleData.columns.map((column) => { +
{ + setScrollOffSet(tableRef.current?.scrollLeft ?? 0); + }}> + {scrollHandle.left ? ( + + ) : null} + {scrollHandle.right ? ( + + ) : null} + +
+ {sampleData?.rows?.length && sampleData?.columns?.length ? ( +
+ + + {sampleData.columns.map((column) => { + return ( + + ); + })} + + + + {sampleData?.rows?.map((row, rowIndex) => { return ( - + + {row.map((data, index) => { + return ( + + ); + })} + ); })} - - - - {sampleData?.rows?.map((row, rowIndex) => { - return ( - - {row.map((data, index) => { - return ( - - ); - })} - - ); - })} - -
+ {column.name} + + ({lowerCase(column.dataType)}) + +
- {column.name} - - ({lowerCase(column.dataType)}) - -
+ {data ? data.toString() : '--'} +
- {data ? data.toString() : '--'} -
- ) : ( -
- No sample data available -
- )} + + + ) : ( +
+ No sample data available +
+ )} +
); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx index 166969c52ae..40a09b5052f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/SchemaTab/SchemaTab.component.tsx @@ -11,11 +11,8 @@ * limitations under the License. */ -import { isNil, isUndefined, lowerCase } from 'lodash'; -import { DatasetSchemaTableTab } from 'Models'; -import React, { FunctionComponent, useEffect, useState } from 'react'; -import { useHistory, useParams } from 'react-router'; -import { getDatasetTabPath } from '../../constants/constants'; +import { lowerCase } from 'lodash'; +import React, { Fragment, FunctionComponent, useState } from 'react'; import { ColumnJoins, Table, @@ -23,9 +20,6 @@ import { } from '../../generated/entity/data/table'; import Searchbar from '../common/searchbar/Searchbar'; import EntityTable from '../EntityTable/EntityTable.component'; -import SampleDataTable, { - SampleColumns, -} from '../SampleDataTable/SampleDataTable.component'; type Props = { owner?: Table['owner']; @@ -42,127 +36,46 @@ const SchemaTab: FunctionComponent = ({ columns, joins, onUpdate, - sampleData, columnName, hasEditAccess, owner, isReadOnly = false, }: Props) => { - const history = useHistory(); - const { datasetFQN: tableFQN, tab } = useParams() as Record; const [searchText, setSearchText] = useState(''); - const [checkedValue, setCheckedValue] = - useState('schema'); const handleSearchAction = (searchValue: string) => { setSearchText(searchValue); }; - const handleToggleChange = (value: DatasetSchemaTableTab) => { - setCheckedValue(value); - history.push({ - pathname: getDatasetTabPath(tableFQN, value), - }); - }; - - useEffect(() => { - if (tab && ['schema', 'sample_data'].includes(tab)) { - const activeTab = isUndefined(tab) - ? 'schema' - : (tab as DatasetSchemaTableTab); - setCheckedValue(activeTab); - } - }, [tab]); - - const getToggleButtonClasses = (type: string): string => { - return ( - 'tw-flex-1 tw-font-medium tw-border tw-border-transparent tw-rounded tw-py-1 tw-px-2 focus:tw-outline-none' + - (type === checkedValue - ? ' tw-bg-primary tw-text-white tw-border-main' - : '') - ); - }; - - const getSampleDataWithType = () => { - const updatedColumns = sampleData?.columns?.map((column) => { - const matchedColumn = columns.find((col) => col.name === column); - - if (matchedColumn) { - return { - name: matchedColumn.name, - dataType: matchedColumn.dataType, - }; - } else { - return { - name: column, - dataType: '', - }; - } - }); - - return { - columns: updatedColumns as SampleColumns[] | undefined, - rows: sampleData?.rows, - }; - }; - return ( -
+
- {checkedValue === 'schema' && ( - - )} +
- {!isReadOnly && !isNil(sampleData) ? ( -
-
- - -
-
- ) : null}
{columns?.length > 0 ? (
- {checkedValue === 'schema' ? ( - - ) : ( - - )} +
) : null}
-
+ ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DatasetDetailsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DatasetDetailsUtils.ts index dae174e81db..0f8c0e99f57 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DatasetDetailsUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DatasetDetailsUtils.ts @@ -28,6 +28,10 @@ export const datasetTableTabs = [ name: 'DBT', path: 'dbt', }, + { + name: 'Sample Data', + path: 'sample_data', + }, { name: 'Manage', path: 'manage', @@ -50,13 +54,17 @@ export const getCurrentDatasetTab = (tab: string) => { break; - case 'manage': + case 'sample_data': currentTab = 5; break; + case 'manage': + currentTab = 6; + + break; + case 'schema': - case 'sample_data': default: currentTab = 1; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx index 8ceb67d1eff..25f4102247e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx @@ -101,6 +101,8 @@ import IconVersionBlack from '../assets/svg/version-black.svg'; import IconVersionWhite from '../assets/svg/version-white.svg'; import IconVersion from '../assets/svg/version.svg'; import IconWarning from '../assets/svg/warning.svg'; +import IconSampleDataColor from '../assets/svg/sample-data-colored.svg'; +import IconSampleData from '../assets/svg/sample-data.svg'; type Props = { alt: string; @@ -199,6 +201,8 @@ export const Icons = { PROFILERCOLOR: 'icon-profilercolor', MANAGECOLOR: 'icon-managecolor', SEARCHV1COLOR: 'icon-searchv1color', + SAMPLE_DATA: 'sample-data', + SAMPLE_DATA_COLOR: 'sample-data-color', }; const SVGIcons: FunctionComponent = ({ @@ -568,6 +572,14 @@ const SVGIcons: FunctionComponent = ({ case Icons.SEARCHV1COLOR: IconComponent = IconSearchV1Color; + break; + case Icons.SAMPLE_DATA: + IconComponent = IconSampleData; + + break; + case Icons.SAMPLE_DATA_COLOR: + IconComponent = IconSampleDataColor; + break; default: