Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dataset field level tags to UI #2729

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion web/src/components/core/text/MqText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface OwnProps {
inverse?: boolean
highlight?: boolean
paragraph?: boolean
overflowHidden?: boolean
color?: string
link?: boolean
href?: string
Expand All @@ -48,6 +49,7 @@ const MqText: React.FC<MqTextProps> = ({
link,
linkTo,
paragraph,
overflowHidden,
href,
inverse,
inline,
Expand Down Expand Up @@ -119,6 +121,9 @@ const MqText: React.FC<MqTextProps> = ({
paragraph: {
marginBottom: theme.spacing(2),
},
overflowHidden: {
overflow: 'hidden',
},
}

const conditionalClasses = Object.assign(
Expand All @@ -135,7 +140,8 @@ const MqText: React.FC<MqTextProps> = ({
small ? classesObject.small : {},
link ? classesObject.link : {},
paragraph ? classesObject.paragraph : {},
subheading ? classesObject.subheading : {}
subheading ? classesObject.subheading : {},
overflowHidden ? classesObject.overflowHidden : {}
)

const style = {
Expand Down
4 changes: 3 additions & 1 deletion web/src/components/core/tooltip/MQTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import Tooltip from '@mui/material/Tooltip'
interface MqToolTipProps {
title: string | ReactElement
children: ReactElement
placement?: 'left' | 'right' | 'top'
}

const MQTooltip: React.FC<MqToolTipProps> = ({ title, children }) => {
const MQTooltip: React.FC<MqToolTipProps> = ({ title, children, placement }) => {
const theme = createTheme(useTheme())
return (
<Tooltip
title={title}
placement={placement || 'bottom'}
componentsProps={{
tooltip: {
sx: {
Expand Down
13 changes: 0 additions & 13 deletions web/src/components/datasets/DatasetDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ const DatasetDetailPage: FunctionComponent<IProps> = (props) => {
datasetTags={tags}
datasetName={lineageDataset.name}
namespace={lineageDataset.namespace}
readonly
/>
<Box display={'flex'} justifyContent={'space-between'} mb={2}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
Expand All @@ -158,11 +157,6 @@ const DatasetDetailPage: FunctionComponent<IProps> = (props) => {
{...a11yProps(3)}
disableRipple={true}
/>
<Tab
label={i18next.t('datasets.dataset_tags_tab')}
{...a11yProps(4)}
disableRipple={true}
/>
</Tabs>
</Box>
<Box display={'flex'} alignItems={'center'}>
Expand Down Expand Up @@ -222,13 +216,6 @@ const DatasetDetailPage: FunctionComponent<IProps> = (props) => {
{tabIndex === 1 && <Io />}
{tabIndex === 2 && <DatasetVersions versions={props.versions} />}
{tabIndex === 3 && <DatasetColumnLineage lineageDataset={props.lineageDataset} />}
{tabIndex === 4 && (
<DatasetTags
namespace={props.lineageDataset.namespace}
datasetName={props.lineageDataset.name}
datasetTags={firstVersion.tags}
/>
)}
</Box>
)
}
Expand Down
173 changes: 59 additions & 114 deletions web/src/components/datasets/DatasetInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,29 @@
// Copyright 2018-2023 contributors to the Marquez project
// Copyright 2018-2024 contributors to the Marquez project
// SPDX-License-Identifier: Apache-2.0

import * as Redux from 'redux'
import {
Accordion,
Box,
Card,
CardContent,
Divider,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
} from '@mui/material'
import { Chip, Drawer } from '@mui/material'
import { Field, Run, Tag } from '../../types/api'
import { Field, Run } from '../../types/api'
import { IState } from '../../store/reducers'
import { connect, useSelector } from 'react-redux'
import { createTheme } from '@mui/material/styles'
import { fetchJobFacets, fetchTags, resetFacets } from '../../store/actionCreators'
import { fetchJobFacets, resetFacets } from '../../store/actionCreators'
import { stopWatchDuration } from '../../helpers/time'
import { useTheme } from '@emotion/react'
import AccordionDetails from '@mui/material/AccordionDetails'
import AccordionSummary from '@mui/material/AccordionSummary'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import MQTooltip from '../core/tooltip/MQTooltip'
import Collapse from '@mui/material/Collapse'
import DatasetTags from './DatasetTags'
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'
import MqCode from '../core/code/MqCode'
import MqEmpty from '../core/empty/MqEmpty'
import MqJsonView from '../core/json-view/MqJsonView'
import MqText from '../core/text/MqText'
import React, { FunctionComponent, useEffect, useState } from 'react'
import ReadMoreIcon from '@mui/icons-material/ReadMore'
import RunStatus from '../jobs/RunStatus'

export interface DispatchProps {
Expand All @@ -58,61 +50,50 @@ type DatasetInfoProps = {
} & JobFacetsProps &
DispatchProps

const formatColumnTags = (tags: string[], tag_desc: Tag[]) => {
const theme = createTheme(useTheme())
return (
<>
{tags.map((tag, index) => {
const tagDescription = tag_desc.find((tagItem) => tagItem.name === tag)
const tooltipTitle = tagDescription?.description || 'No Tag Description'
return (
<MQTooltip title={tooltipTitle} key={tag}>
<Chip
label={tag}
size='small'
style={{
display: 'row',
marginRight: index < tags.length - 1 ? theme.spacing(1) : 0,
marginTop: 3,
}}
/>
</MQTooltip>
)
})}
</>
)
}

const DatasetInfo: FunctionComponent<DatasetInfoProps> = (props) => {
const { datasetFields, facets, run, jobFacets, fetchJobFacets, resetFacets } = props
const i18next = require('i18next')
const dsNamespace = useSelector(
(state: IState) => state.datasetVersions.result.versions[0].namespace
)
const dsName = useSelector((state: IState) => state.datasetVersions.result.versions[0].name)

const [open, setOpen] = useState(false)
const [selectedKey, setSelectedKey] = useState<string | undefined>(undefined)
const theme = createTheme(useTheme())
const loadCollapsedState = () => {
const storedState = localStorage.getItem(`dsi_${dsNamespace}_${dsName}`)
return storedState ? JSON.parse(storedState) : []
}

useEffect(() => {
run && fetchJobFacets(run.id)
run && fetchTags()
}, [run])

// unmounting
useEffect(
() => () => {
resetFacets()
},
[]
)
const [expandedRows, setExpandedRows] = useState<number[]>(loadCollapsedState)

const toggleRow = (index: number) => {
setExpandedRows((prevExpandedRows) => {
const newExpandedRows = prevExpandedRows.includes(index)
? prevExpandedRows.filter((rowIndex) => rowIndex !== index)
: [...prevExpandedRows, index]

const tagData = useSelector((state: IState) => state.tags.tags)
const handleOpen = (key: string) => {
setOpen(true)
setSelectedKey(key)
localStorage.setItem(`dsi_${dsNamespace}_${dsName}`, JSON.stringify(newExpandedRows))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of want to get away from doing this because it doesn't move across urls. Could we possibly use use parameters for functionality like this?

return newExpandedRows
})
}

const selectedField = datasetFields.find((field) => field.name === selectedKey)
const selectedFieldTags = selectedField?.tags || []
const selectedFieldDesc = selectedField?.description || 'No Description'
useEffect(() => {
for (const key in localStorage) {
if (key !== `dsi_${dsNamespace}_${dsName}`) {
localStorage.removeItem(key)
}
}
}, [dsNamespace, dsName])

return (
<Box>
Expand Down Expand Up @@ -146,73 +127,38 @@ const DatasetInfo: FunctionComponent<DatasetInfoProps> = (props) => {
</TableRow>
</TableHead>
<TableBody>
{datasetFields.map((field) => {
{datasetFields.map((field, index) => {
return (
<TableRow key={field.name}>
<TableCell align='left'>{field.name}</TableCell>
<TableCell align='left'>{field.type}</TableCell>
<TableCell align='left'>{field.description || 'no description'}</TableCell>
<TableCell>
<ReadMoreIcon
onClick={() => handleOpen(field.name)}
sx={{ align: 'Right' }}
></ReadMoreIcon>
</TableCell>
</TableRow>
<React.Fragment key={field.name}>
<TableRow onClick={() => toggleRow(index)} className='expandable-row'>
<TableCell align='left'>{field.name}</TableCell>
<TableCell align='left'>{field.type}</TableCell>
<TableCell align='left'>{field.description || 'no description'}</TableCell>
<TableCell align='right'>
<KeyboardArrowDownIcon />
</TableCell>
</TableRow>
<TableRow>
<TableCell colSpan={4} style={{ padding: 0, border: 'none' }}>
<Collapse in={expandedRows.includes(index)} timeout='auto'>
<Card>
<CardContent>
<DatasetTags
namespace={dsNamespace}
datasetName={dsName}
datasetTags={field.tags}
datasetField={field.name}
/>
</CardContent>
</Card>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a UI perspective, I think it may be perhaps better to have tags that are already part of a dataset listed.

For new tags, I think a dialog would be pretty nice for this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool - thanks for the feedback. Yeah I agree it's not as slick as I would like. I have refactored and DatasetTags now looks like this:

Screenshot 2024-02-01 at 18 20 19 Screenshot 2024-02-01 at 18 20 48

})}
</TableBody>
</Table>
<Drawer
elevation={0}
anchor='right'
open={open}
onClose={() => setOpen(false)}
sx={{ zIndex: theme.zIndex.drawer + 1 }}
PaperProps={{
sx: {
width: 400,
backgroundColor: theme.palette.background.paper,
border: `2px dashed ${theme.palette.secondary.main}`,
p: 1,
},
}}
>
<Card>
<CardContent sx={{ backgroundColor: theme.palette.background.paper }}>
<MqText heading bottomMargin>
{selectedKey}
</MqText>
</CardContent>
</Card>
<Divider />
<Card>
<CardContent sx={{ backgroundColor: theme.palette.background.paper }}>
<MqText bottomMargin>{selectedFieldDesc}</MqText>
</CardContent>
</Card>
<Accordion elevation={0}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
sx={{
backgroundColor: theme.palette.background.paper,
}}
>
<MqText bold bottomMargin>
Tags
</MqText>
</AccordionSummary>
<AccordionDetails
sx={{
backgroundColor: theme.palette.background.paper,
}}
>
{selectedFieldTags.length > 0
? formatColumnTags(selectedFieldTags, tagData)
: 'No Tags'}
</AccordionDetails>
</Accordion>
</Drawer>
</>
)}
{facets && (
Expand Down Expand Up @@ -260,7 +206,6 @@ const mapDispatchToProps = (dispatch: Redux.Dispatch) =>
{
fetchJobFacets: fetchJobFacets,
resetFacets: resetFacets,
fetchTags: fetchTags,
},
dispatch
)
Expand Down
Loading
Loading