-
Notifications
You must be signed in to change notification settings - Fork 323
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
Changes from 7 commits
52c70fa
bc6cfe2
0c68a8a
c566656
bbe46c0
002a5dd
2a6fd3b
f14f414
b61cddc
049bc1b
bf2e372
4889bf4
67d2e12
12dbad3
84b0364
a54067e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 { | ||
|
@@ -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)) | ||
|
||
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> | ||
|
@@ -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> | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
})} | ||
</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 && ( | ||
|
@@ -260,7 +206,6 @@ const mapDispatchToProps = (dispatch: Redux.Dispatch) => | |
{ | ||
fetchJobFacets: fetchJobFacets, | ||
resetFacets: resetFacets, | ||
fetchTags: fetchTags, | ||
}, | ||
dispatch | ||
) | ||
|
There was a problem hiding this comment.
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?