Skip to content
Draft
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
5 changes: 5 additions & 0 deletions table/schemas/table.cue
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ spec: close({
width?: number | "auto"
hide?: bool
cellSettings?: [...#cellSettings]
dataLink?: {
url: string
title?: string
openNewTab: bool
}
}

#valueCondition: {
Expand Down
184 changes: 183 additions & 1 deletion table/src/components/ColumnsEditor/ColumnEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { Button, ButtonGroup, Stack, StackProps, Switch, TextField } from '@mui/material';
import {
Button,
ButtonGroup,
DialogActions,
DialogContent,
DialogTitle,
FormControl,
FormLabel,
IconButton,
Stack,
StackProps,
Switch,
TextField,
Typography,
} from '@mui/material';
import { ReactElement, useState } from 'react';
import {
AlignSelector,
Expand All @@ -21,9 +35,14 @@ import {
OptionsEditorGrid,
OptionsEditorGroup,
SortSelectorButtons,
Dialog,
} from '@perses-dev/components';
import { FormatOptions } from '@perses-dev/core';
import { PluginKindSelect } from '@perses-dev/plugin-system';
import ContentCopyIcon from 'mdi-material-ui/ContentCopy';
import DeleteIcon from 'mdi-material-ui/Delete';
import InformationIcon from 'mdi-material-ui/Information';
import LinkIcon from 'mdi-material-ui/Link';
import { ColumnSettings } from '../../models';
import { ConditionalPanel } from '../ConditionalPanel';

Expand All @@ -39,13 +58,136 @@ export interface ColumnEditorProps extends Omit<StackProps, OmittedMuiProps> {
onChange: (column: ColumnSettings) => void;
}

type LinkManagementDialogueProps = Pick<ColumnEditorProps, 'onChange' | 'column'> & {
actionTitle: string;
open: boolean;
key?: string;
setOpen: (value: { open: boolean }) => void;
};
const LinkManagementDialog = (props: LinkManagementDialogueProps): ReactElement => {
const {
actionTitle,
open,
key,
column: { dataLink },
column,
onChange,
setOpen,
} = props;

const [url, setUrl] = useState(dataLink?.url);
const [title, setTitle] = useState(dataLink?.title);
const [openNewTab, setOpenNewTab] = useState(!!dataLink?.openNewTab);
const [urlError, setUrlError] = useState<{ hasError: boolean; helperText: string } | undefined>(undefined);

const handleSaveDataLink = (): void => {
if (!url) {
setUrlError({ hasError: true, helperText: 'Url Can not be empty' });
return;
}
onChange({ ...column, dataLink: { url, title, openNewTab } });
setOpen({ open: false });
};

return (
<Dialog
Copy link
Member

Choose a reason for hiding this comment

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

Use Perses Dialog component

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is Perses
The last item.

import {
  AlignSelector,
  FormatControls,
  OptionsEditorColumn,
  OptionsEditorControl,
  OptionsEditorGrid,
  OptionsEditorGroup,
  SortSelectorButtons,
  Dialog,
} from '@perses-dev/components';

Copy link
Member

Choose a reason for hiding this comment

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

Ok, I'm crazy I though I saw it in MUI import 😓

key={key}
sx={{
'& .MuiDialog-paper': {
width: '80vw',
},
}}
open={open}
>
<DialogTitle>{actionTitle}</DialogTitle>
<DialogContent>
<Stack spacing={2}>
<FormControl>
Copy link
Member

@Gladorme Gladorme Nov 27, 2025

Choose a reason for hiding this comment

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

We already have a form for editing links LinksEditor/LinkControl, can it be re-used (UI consistency and code re-usage)?

<TextField
error={urlError?.hasError}
helperText={urlError?.hasError ? urlError?.helperText : undefined}
label="URL"
sx={{ marginTop: '12px' }}
multiline
maxRows={5}
onChange={(e) => {
if (urlError) setUrlError(undefined);
setUrl(e.target.value);
}}
type="url"
placeholder="http://target.com/x/{column_name}/z"
value={url}
/>
</FormControl>

<FormControl>
<TextField
label="Title"
onChange={(e) => {
setTitle(e.target.value);
}}
placeholder="Title"
type="text"
value={title}
/>
</FormControl>
<FormControl>
<FormLabel>Open in new tab</FormLabel>
<FormControl>
<Switch
onChange={(e) => {
setOpenNewTab(e.target.checked);
}}
checked={openNewTab}
/>
</FormControl>
</FormControl>
<FormControl>
<Stack direction="row" spacing={1} alignItems="center">
<InformationIcon fontSize="small" />
<Typography variant="body1">
You can create dynamic links using column names wrapped with curly braces
</Typography>
</Stack>
</FormControl>
</Stack>
</DialogContent>
<DialogActions>
<Button variant="contained" onClick={handleSaveDataLink}>
Save
</Button>
<Button
onClick={() => {
setOpen({ open: false });
}}
>
Cancel
</Button>
</DialogActions>
</Dialog>
);
};

export function ColumnEditor({ column, onChange, ...others }: ColumnEditorProps): ReactElement {
const [width, setWidth] = useState<number>(
column.width === undefined || column.width === 'auto' ? 100 : column.width
);

const [openAddLinkDialog, setOpenAddLinkDialog] = useState<{ open: boolean; key?: string }>({
open: false,
});
const linkManagementAction = column?.dataLink ? 'Edit Column Link' : 'Add Column Link';

return (
<Stack {...others}>
<LinkManagementDialog
actionTitle={linkManagementAction}
onChange={onChange}
column={column}
open={openAddLinkDialog.open}
key={openAddLinkDialog.key}
setOpen={setOpenAddLinkDialog}
/>
<OptionsEditorGrid>
<OptionsEditorColumn>
<OptionsEditorGroup title="Column">
Expand Down Expand Up @@ -206,6 +348,46 @@ export function ColumnEditor({ column, onChange, ...others }: ColumnEditorProps)
)}
</OptionsEditorGroup>
</OptionsEditorColumn>
<OptionsEditorColumn>
<OptionsEditorGroup title="Link and Actions">
<OptionsEditorControl
label="Column link"
control={
<Button
startIcon={<LinkIcon />}
onClick={(): void => {
setOpenAddLinkDialog({ open: true, key: String(Date.now()) });
}}
>
{linkManagementAction}
</Button>
}
/>
{column?.dataLink?.url && (
<Stack direction="row" spacing={1} alignItems="center" alignContent="center">
<Typography sx={{ flexGrow: 1, minWidth: 0 }} component="span" variant="body1" noWrap>
{column?.dataLink?.title || column?.dataLink?.url}
</Typography>
<IconButton
onClick={(): void => {
if (column?.dataLink?.url) navigator.clipboard.writeText(column?.dataLink?.url);
}}
size="small"
>
<ContentCopyIcon fontSize="small" sx={{ verticalAlign: 'middle' }} />
</IconButton>
<IconButton
onClick={(): void => {
onChange({ ...column, dataLink: undefined });
}}
size="small"
>
<DeleteIcon fontSize="small" sx={{ verticalAlign: 'middle' }} />
</IconButton>
</Stack>
)}
</OptionsEditorGroup>
</OptionsEditorColumn>
</OptionsEditorGrid>
<Stack sx={{ px: 8 }}>
<OptionsEditorGroup title="Conditional Cell Format">
Expand Down
6 changes: 6 additions & 0 deletions table/src/models/table-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ export interface ColumnSettings {
hide?: boolean;
// Customize cell display based on their value for this specific column.
cellSettings?: CellSettings[];

dataLink?: {
url: string;
title?: string;
openNewTab: boolean;
};
}

export interface ValueCondition {
Expand Down