Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { Meta, StoryFn } from "@storybook/react";
import { MethodRow as MethodRowComponent } from "../../view/model-editor/MethodRow";
import { CallClassification, Method } from "../../model-editor/method";
import { ModeledMethod } from "../../model-editor/modeled-method";
import { VSCodeDataGrid } from "@vscode/webview-ui-toolkit/react";
import {
MULTIPLE_MODELS_GRID_TEMPLATE_COLUMNS,
SINGLE_MODEL_GRID_TEMPLATE_COLUMNS,
} from "../../view/model-editor/ModeledMethodDataGrid";
import { ModelEditorViewState } from "../../model-editor/shared/view-state";
import { createMockExtensionPack } from "../../../test/factories/model-editor/extension-pack";
import { Mode } from "../../model-editor/shared/mode";
import { DataGrid } from "../../view/common/DataGrid";

export default {
title: "CodeQL Model Editor/Method Row",
Expand All @@ -24,9 +24,9 @@ const Template: StoryFn<typeof MethodRowComponent> = (args) => {
? MULTIPLE_MODELS_GRID_TEMPLATE_COLUMNS
: SINGLE_MODEL_GRID_TEMPLATE_COLUMNS;
return (
<VSCodeDataGrid gridTemplateColumns={gridTemplateColumns}>
<DataGrid gridTemplateColumns={gridTemplateColumns}>
<MethodRowComponent {...args} />
</VSCodeDataGrid>
</DataGrid>
);
};

Expand Down
127 changes: 127 additions & 0 deletions extensions/ql-vscode/src/view/common/DataGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import * as React from "react";
import { ReactNode, forwardRef } from "react";
import { styled } from "styled-components";

/*
* A drop-in replacement for the VSCodeDataGrid family of components.
*
* The difference is that the `display: grid` styling is applied to `DataGrid`, whereas
* in the VS Code version that styling is applied to `VSCodeDataGridRow`. This gives
* column alignment across rows in situation with dynamic contents. It also allows
* for cells to span multiple rows and all the other features of data grids.
*/

const StyledDataGrid = styled.div<{ $gridTemplateColumns: string | number }>`
Comment thread
robertbrignull marked this conversation as resolved.
display: grid;
grid-template-columns: ${(props) => props.$gridTemplateColumns};
box-sizing: border-box;
width: 100%;
background: transparent;
`;

interface DataGridProps {
gridTemplateColumns: string;
children: ReactNode;
}

/**
* The top level for a grid systemm that will contain `DataGridRow` and `DataGridCell` components.
*
* See https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns for how to use `gridTemplateColumns`.
*/
export function DataGrid({ gridTemplateColumns, children }: DataGridProps) {
return (
<StyledDataGrid $gridTemplateColumns={gridTemplateColumns}>
{children}
</StyledDataGrid>
);
}

const StyledDataGridRow = styled.div<{ $focused?: boolean }>`
display: contents;

&:hover > * {
background-color: var(--list-hover-background);
}

& > * {
// Use !important to override the background color set by the hover state
background-color: ${(props) =>
props.$focused
? "var(--vscode-editor-selectionBackground) !important"
: "inherit"};
}
`;

interface DataGridRowProps {
focused?: boolean;
children: ReactNode;
"data-testid"?: string;
}

/**
* Optional component for encompasing a single row in a `DataGrid`.
* Implements hover and focus states that highlight all cells in the row.
*
* Note that using this component is not mandatory. Cells can be placed directly
* inside a `DataGrid`. Feel free to skip this component if your cells do not
* line up into neat rows, or you do not need the hover and focus states.
*/
export const DataGridRow = forwardRef(
Comment thread
koesie10 marked this conversation as resolved.
(
{ focused, children, "data-testid": testId }: DataGridRowProps,
ref?: React.Ref<HTMLElement | undefined>,
) => (
<StyledDataGridRow $focused={focused} ref={ref} data-testid={testId}>
{children}
</StyledDataGridRow>
),
);
DataGridRow.displayName = "DataGridRow";

const StyledDataGridCell = styled.div<{
$rowType: "default" | "header";
$gridRow?: string | number;
$gridColumn?: string | number;
}>`
${({ $rowType }) => ($rowType === "header" ? "font-weight: 600;" : "")}
${({ $gridRow }) => ($gridRow ? `grid-row: ${$gridRow};` : "")}
${({ $gridColumn }) => ($gridColumn ? `grid-column: ${$gridColumn};` : "")}
padding: 4px 12px;
`;

interface DataGridCellProps {
rowType?: "default" | "header";
gridRow?: string | number;
gridColumn?: string | number;
className?: string;
children: ReactNode;
}

/**
* A cell in a `DataGrid`.
*
* By default, the position of cells in the grid is determined by the order in which
* they appear in the DOM. Cells will fill up the current row and then move on to the
* next row. This can be overridden using the `gridRow` and `gridColumn` to place
* cells anywhere within the grid. You can also configure cells to span multiple rows
* or columns. See https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column.
*/
export function DataGridCell({
rowType = "default",
gridRow,
gridColumn,
className,
children,
}: DataGridCellProps) {
return (
<StyledDataGridCell
$rowType={rowType}
$gridRow={gridRow}
$gridColumn={gridColumn}
className={className}
>
{children}
</StyledDataGridCell>
);
}
18 changes: 10 additions & 8 deletions extensions/ql-vscode/src/view/model-editor/HiddenMethodsRow.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,37 @@
import {
VSCodeDataGridCell,
VSCodeDataGridRow,
} from "@vscode/webview-ui-toolkit/react";
import * as React from "react";
import { styled } from "styled-components";
import { pluralize } from "../../common/word";
import { DataGridCell, DataGridRow } from "../common/DataGrid";
import { ModelEditorViewState } from "../../model-editor/shared/view-state";

const HiddenMethodsCell = styled(VSCodeDataGridCell)`
Comment thread
robertbrignull marked this conversation as resolved.
const HiddenMethodsCell = styled(DataGridCell)`
text-align: center;
`;

interface Props {
numHiddenMethods: number;
someMethodsAreVisible: boolean;
viewState: ModelEditorViewState;
}

export function HiddenMethodsRow({
numHiddenMethods,
someMethodsAreVisible,
viewState,
}: Props) {
if (numHiddenMethods === 0) {
return null;
}

const gridColumn = viewState.showMultipleModels ? "span 6" : "span 5";

return (
<VSCodeDataGridRow>
<HiddenMethodsCell gridColumn="span 5">
<DataGridRow>
<HiddenMethodsCell gridColumn={gridColumn}>
{someMethodsAreVisible && "And "}
{pluralize(numHiddenMethods, "method", "methods")} modeled in other
CodeQL packs
</HiddenMethodsCell>
</VSCodeDataGridRow>
</DataGridRow>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Method } from "../../model-editor/method";

const Name = styled.span`
font-family: var(--vscode-editor-font-family);
word-break: break-all;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I had to add this otherwise it was breaking the grid at low screen widths. I'm not sure why this is needed now and wasn't before.

`;

export const MethodName = (method: Method): JSX.Element => {
Expand Down
52 changes: 22 additions & 30 deletions extensions/ql-vscode/src/view/model-editor/MethodRow.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import {
VSCodeButton,
VSCodeDataGridCell,
VSCodeDataGridRow,
VSCodeLink,
VSCodeProgressRing,
} from "@vscode/webview-ui-toolkit/react";
Expand All @@ -25,8 +23,9 @@ import { ModelOutputDropdown } from "./ModelOutputDropdown";
import { ModelEditorViewState } from "../../model-editor/shared/view-state";
import { Codicon } from "../common";
import { canAddNewModeledMethod } from "../../model-editor/shared/multiple-modeled-methods";
import { DataGridCell, DataGridRow } from "../common/DataGrid";

const MultiModelColumn = styled(VSCodeDataGridCell)`
const MultiModelColumn = styled(DataGridCell)`
display: flex;
flex-direction: column;
gap: 0.5em;
Expand Down Expand Up @@ -63,11 +62,6 @@ const CodiconRow = styled(VSCodeButton)`
align-items: center;
`;

const DataGridRow = styled(VSCodeDataGridRow)<{ focused?: boolean }>`
outline: ${(props) =>
props.focused ? "1px solid var(--vscode-focusBorder)" : "none"};
`;

export type MethodRowProps = {
method: Method;
methodCanBeModeled: boolean;
Expand Down Expand Up @@ -168,7 +162,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
ref={ref}
focused={revealedMethodSignature === method.signature}
>
<VSCodeDataGridCell gridColumn={1}>
<DataGridCell>
<ApiOrMethodRow>
<ModelingStatusIndicator status={modelingStatus} />
<MethodClassifications method={method} />
Expand All @@ -181,33 +175,33 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
<ViewLink onClick={jumpToMethod}>View</ViewLink>
{props.modelingInProgress && <ProgressRing />}
</ApiOrMethodRow>
</VSCodeDataGridCell>
</DataGridCell>
{props.modelingInProgress && (
<>
<VSCodeDataGridCell gridColumn={2}>
<DataGridCell>
<InProgressDropdown />
</VSCodeDataGridCell>
<VSCodeDataGridCell gridColumn={3}>
</DataGridCell>
<DataGridCell>
<InProgressDropdown />
</VSCodeDataGridCell>
<VSCodeDataGridCell gridColumn={4}>
</DataGridCell>
<DataGridCell>
<InProgressDropdown />
</VSCodeDataGridCell>
<VSCodeDataGridCell gridColumn={5}>
</DataGridCell>
<DataGridCell>
<InProgressDropdown />
</VSCodeDataGridCell>
</DataGridCell>
{viewState.showMultipleModels && (
<VSCodeDataGridCell gridColumn={6}>
<DataGridCell>
<CodiconRow appearance="icon" disabled={true}>
<Codicon name="add" label="Add new model" />
</CodiconRow>
</VSCodeDataGridCell>
</DataGridCell>
)}
</>
)}
{!props.modelingInProgress && (
<>
<MultiModelColumn gridColumn={2}>
<MultiModelColumn>
{modeledMethods.map((modeledMethod, index) => (
<ModelTypeDropdown
key={index}
Expand All @@ -217,7 +211,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
/>
))}
</MultiModelColumn>
<MultiModelColumn gridColumn={3}>
<MultiModelColumn>
{modeledMethods.map((modeledMethod, index) => (
<ModelInputDropdown
key={index}
Expand All @@ -227,7 +221,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
/>
))}
</MultiModelColumn>
<MultiModelColumn gridColumn={4}>
<MultiModelColumn>
{modeledMethods.map((modeledMethod, index) => (
<ModelOutputDropdown
key={index}
Expand All @@ -237,7 +231,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
/>
))}
</MultiModelColumn>
<MultiModelColumn gridColumn={5}>
<MultiModelColumn>
{modeledMethods.map((modeledMethod, index) => (
<ModelKindDropdown
key={index}
Expand All @@ -248,7 +242,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
))}
</MultiModelColumn>
{viewState.showMultipleModels && (
<MultiModelColumn gridColumn={6}>
<MultiModelColumn>
{modeledMethods.map((_, index) =>
index === modeledMethods.length - 1 ? (
<CodiconRow
Expand Down Expand Up @@ -298,7 +292,7 @@ const UnmodelableMethodRow = forwardRef<
ref={ref}
focused={revealedMethodSignature === method.signature}
>
<VSCodeDataGridCell gridColumn={1}>
<DataGridCell>
<ApiOrMethodRow>
<ModelingStatusIndicator status="saved" />
<MethodName {...props.method} />
Expand All @@ -310,10 +304,8 @@ const UnmodelableMethodRow = forwardRef<
<ViewLink onClick={jumpToMethod}>View</ViewLink>
<MethodClassifications method={method} />
</ApiOrMethodRow>
</VSCodeDataGridCell>
<VSCodeDataGridCell gridColumn="span 4">
Method already modeled
</VSCodeDataGridCell>
</DataGridCell>
<DataGridCell gridColumn="span 4">Method already modeled</DataGridCell>
</DataGridRow>
);
});
Expand Down
Loading