Skip to content

Commit 947f495

Browse files
Merge pull request #2990 from github/robertbrignull/model-table-alignment
Use custom grid element instead of VSCodeDataGrid
2 parents df55e03 + c88ecf7 commit 947f495

File tree

7 files changed

+232
-91
lines changed

7 files changed

+232
-91
lines changed

extensions/ql-vscode/src/stories/model-editor/MethodRow.stories.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import { Meta, StoryFn } from "@storybook/react";
55
import { MethodRow as MethodRowComponent } from "../../view/model-editor/MethodRow";
66
import { CallClassification, Method } from "../../model-editor/method";
77
import { ModeledMethod } from "../../model-editor/modeled-method";
8-
import { VSCodeDataGrid } from "@vscode/webview-ui-toolkit/react";
98
import {
109
MULTIPLE_MODELS_GRID_TEMPLATE_COLUMNS,
1110
SINGLE_MODEL_GRID_TEMPLATE_COLUMNS,
1211
} from "../../view/model-editor/ModeledMethodDataGrid";
1312
import { ModelEditorViewState } from "../../model-editor/shared/view-state";
1413
import { createMockExtensionPack } from "../../../test/factories/model-editor/extension-pack";
1514
import { Mode } from "../../model-editor/shared/mode";
15+
import { DataGrid } from "../../view/common/DataGrid";
1616

1717
export default {
1818
title: "CodeQL Model Editor/Method Row",
@@ -24,9 +24,9 @@ const Template: StoryFn<typeof MethodRowComponent> = (args) => {
2424
? MULTIPLE_MODELS_GRID_TEMPLATE_COLUMNS
2525
: SINGLE_MODEL_GRID_TEMPLATE_COLUMNS;
2626
return (
27-
<VSCodeDataGrid gridTemplateColumns={gridTemplateColumns}>
27+
<DataGrid gridTemplateColumns={gridTemplateColumns}>
2828
<MethodRowComponent {...args} />
29-
</VSCodeDataGrid>
29+
</DataGrid>
3030
);
3131
};
3232

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import * as React from "react";
2+
import { ReactNode, forwardRef } from "react";
3+
import { styled } from "styled-components";
4+
5+
/*
6+
* A drop-in replacement for the VSCodeDataGrid family of components.
7+
*
8+
* The difference is that the `display: grid` styling is applied to `DataGrid`, whereas
9+
* in the VS Code version that styling is applied to `VSCodeDataGridRow`. This gives
10+
* column alignment across rows in situation with dynamic contents. It also allows
11+
* for cells to span multiple rows and all the other features of data grids.
12+
*/
13+
14+
const StyledDataGrid = styled.div<{ $gridTemplateColumns: string | number }>`
15+
display: grid;
16+
grid-template-columns: ${(props) => props.$gridTemplateColumns};
17+
box-sizing: border-box;
18+
width: 100%;
19+
background: transparent;
20+
`;
21+
22+
interface DataGridProps {
23+
gridTemplateColumns: string;
24+
children: ReactNode;
25+
}
26+
27+
/**
28+
* The top level for a grid systemm that will contain `DataGridRow` and `DataGridCell` components.
29+
*
30+
* See https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns for how to use `gridTemplateColumns`.
31+
*/
32+
export function DataGrid({ gridTemplateColumns, children }: DataGridProps) {
33+
return (
34+
<StyledDataGrid $gridTemplateColumns={gridTemplateColumns}>
35+
{children}
36+
</StyledDataGrid>
37+
);
38+
}
39+
40+
const StyledDataGridRow = styled.div<{ $focused?: boolean }>`
41+
display: contents;
42+
43+
&:hover > * {
44+
background-color: var(--list-hover-background);
45+
}
46+
47+
& > * {
48+
// Use !important to override the background color set by the hover state
49+
background-color: ${(props) =>
50+
props.$focused
51+
? "var(--vscode-editor-selectionBackground) !important"
52+
: "inherit"};
53+
}
54+
`;
55+
56+
interface DataGridRowProps {
57+
focused?: boolean;
58+
children: ReactNode;
59+
"data-testid"?: string;
60+
}
61+
62+
/**
63+
* Optional component for encompasing a single row in a `DataGrid`.
64+
* Implements hover and focus states that highlight all cells in the row.
65+
*
66+
* Note that using this component is not mandatory. Cells can be placed directly
67+
* inside a `DataGrid`. Feel free to skip this component if your cells do not
68+
* line up into neat rows, or you do not need the hover and focus states.
69+
*/
70+
export const DataGridRow = forwardRef(
71+
(
72+
{ focused, children, "data-testid": testId }: DataGridRowProps,
73+
ref?: React.Ref<HTMLElement | undefined>,
74+
) => (
75+
<StyledDataGridRow $focused={focused} ref={ref} data-testid={testId}>
76+
{children}
77+
</StyledDataGridRow>
78+
),
79+
);
80+
DataGridRow.displayName = "DataGridRow";
81+
82+
const StyledDataGridCell = styled.div<{
83+
$rowType: "default" | "header";
84+
$gridRow?: string | number;
85+
$gridColumn?: string | number;
86+
}>`
87+
${({ $rowType }) => ($rowType === "header" ? "font-weight: 600;" : "")}
88+
${({ $gridRow }) => ($gridRow ? `grid-row: ${$gridRow};` : "")}
89+
${({ $gridColumn }) => ($gridColumn ? `grid-column: ${$gridColumn};` : "")}
90+
padding: 4px 12px;
91+
`;
92+
93+
interface DataGridCellProps {
94+
rowType?: "default" | "header";
95+
gridRow?: string | number;
96+
gridColumn?: string | number;
97+
className?: string;
98+
children: ReactNode;
99+
}
100+
101+
/**
102+
* A cell in a `DataGrid`.
103+
*
104+
* By default, the position of cells in the grid is determined by the order in which
105+
* they appear in the DOM. Cells will fill up the current row and then move on to the
106+
* next row. This can be overridden using the `gridRow` and `gridColumn` to place
107+
* cells anywhere within the grid. You can also configure cells to span multiple rows
108+
* or columns. See https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column.
109+
*/
110+
export function DataGridCell({
111+
rowType = "default",
112+
gridRow,
113+
gridColumn,
114+
className,
115+
children,
116+
}: DataGridCellProps) {
117+
return (
118+
<StyledDataGridCell
119+
$rowType={rowType}
120+
$gridRow={gridRow}
121+
$gridColumn={gridColumn}
122+
className={className}
123+
>
124+
{children}
125+
</StyledDataGridCell>
126+
);
127+
}
Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,37 @@
1-
import {
2-
VSCodeDataGridCell,
3-
VSCodeDataGridRow,
4-
} from "@vscode/webview-ui-toolkit/react";
51
import * as React from "react";
62
import { styled } from "styled-components";
73
import { pluralize } from "../../common/word";
4+
import { DataGridCell, DataGridRow } from "../common/DataGrid";
5+
import { ModelEditorViewState } from "../../model-editor/shared/view-state";
86

9-
const HiddenMethodsCell = styled(VSCodeDataGridCell)`
7+
const HiddenMethodsCell = styled(DataGridCell)`
108
text-align: center;
119
`;
1210

1311
interface Props {
1412
numHiddenMethods: number;
1513
someMethodsAreVisible: boolean;
14+
viewState: ModelEditorViewState;
1615
}
1716

1817
export function HiddenMethodsRow({
1918
numHiddenMethods,
2019
someMethodsAreVisible,
20+
viewState,
2121
}: Props) {
2222
if (numHiddenMethods === 0) {
2323
return null;
2424
}
2525

26+
const gridColumn = viewState.showMultipleModels ? "span 6" : "span 5";
27+
2628
return (
27-
<VSCodeDataGridRow>
28-
<HiddenMethodsCell gridColumn="span 5">
29+
<DataGridRow>
30+
<HiddenMethodsCell gridColumn={gridColumn}>
2931
{someMethodsAreVisible && "And "}
3032
{pluralize(numHiddenMethods, "method", "methods")} modeled in other
3133
CodeQL packs
3234
</HiddenMethodsCell>
33-
</VSCodeDataGridRow>
35+
</DataGridRow>
3436
);
3537
}

extensions/ql-vscode/src/view/model-editor/MethodName.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Method } from "../../model-editor/method";
44

55
const Name = styled.span`
66
font-family: var(--vscode-editor-font-family);
7+
word-break: break-all;
78
`;
89

910
export const MethodName = (method: Method): JSX.Element => {

extensions/ql-vscode/src/view/model-editor/MethodRow.tsx

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import {
22
VSCodeButton,
3-
VSCodeDataGridCell,
4-
VSCodeDataGridRow,
53
VSCodeLink,
64
VSCodeProgressRing,
75
} from "@vscode/webview-ui-toolkit/react";
@@ -25,8 +23,9 @@ import { ModelOutputDropdown } from "./ModelOutputDropdown";
2523
import { ModelEditorViewState } from "../../model-editor/shared/view-state";
2624
import { Codicon } from "../common";
2725
import { canAddNewModeledMethod } from "../../model-editor/shared/multiple-modeled-methods";
26+
import { DataGridCell, DataGridRow } from "../common/DataGrid";
2827

29-
const MultiModelColumn = styled(VSCodeDataGridCell)`
28+
const MultiModelColumn = styled(DataGridCell)`
3029
display: flex;
3130
flex-direction: column;
3231
gap: 0.5em;
@@ -63,11 +62,6 @@ const CodiconRow = styled(VSCodeButton)`
6362
align-items: center;
6463
`;
6564

66-
const DataGridRow = styled(VSCodeDataGridRow)<{ focused?: boolean }>`
67-
outline: ${(props) =>
68-
props.focused ? "1px solid var(--vscode-focusBorder)" : "none"};
69-
`;
70-
7165
export type MethodRowProps = {
7266
method: Method;
7367
methodCanBeModeled: boolean;
@@ -168,7 +162,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
168162
ref={ref}
169163
focused={revealedMethodSignature === method.signature}
170164
>
171-
<VSCodeDataGridCell gridColumn={1}>
165+
<DataGridCell>
172166
<ApiOrMethodRow>
173167
<ModelingStatusIndicator status={modelingStatus} />
174168
<MethodClassifications method={method} />
@@ -181,33 +175,33 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
181175
<ViewLink onClick={jumpToMethod}>View</ViewLink>
182176
{props.modelingInProgress && <ProgressRing />}
183177
</ApiOrMethodRow>
184-
</VSCodeDataGridCell>
178+
</DataGridCell>
185179
{props.modelingInProgress && (
186180
<>
187-
<VSCodeDataGridCell gridColumn={2}>
181+
<DataGridCell>
188182
<InProgressDropdown />
189-
</VSCodeDataGridCell>
190-
<VSCodeDataGridCell gridColumn={3}>
183+
</DataGridCell>
184+
<DataGridCell>
191185
<InProgressDropdown />
192-
</VSCodeDataGridCell>
193-
<VSCodeDataGridCell gridColumn={4}>
186+
</DataGridCell>
187+
<DataGridCell>
194188
<InProgressDropdown />
195-
</VSCodeDataGridCell>
196-
<VSCodeDataGridCell gridColumn={5}>
189+
</DataGridCell>
190+
<DataGridCell>
197191
<InProgressDropdown />
198-
</VSCodeDataGridCell>
192+
</DataGridCell>
199193
{viewState.showMultipleModels && (
200-
<VSCodeDataGridCell gridColumn={6}>
194+
<DataGridCell>
201195
<CodiconRow appearance="icon" disabled={true}>
202196
<Codicon name="add" label="Add new model" />
203197
</CodiconRow>
204-
</VSCodeDataGridCell>
198+
</DataGridCell>
205199
)}
206200
</>
207201
)}
208202
{!props.modelingInProgress && (
209203
<>
210-
<MultiModelColumn gridColumn={2}>
204+
<MultiModelColumn>
211205
{modeledMethods.map((modeledMethod, index) => (
212206
<ModelTypeDropdown
213207
key={index}
@@ -217,7 +211,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
217211
/>
218212
))}
219213
</MultiModelColumn>
220-
<MultiModelColumn gridColumn={3}>
214+
<MultiModelColumn>
221215
{modeledMethods.map((modeledMethod, index) => (
222216
<ModelInputDropdown
223217
key={index}
@@ -227,7 +221,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
227221
/>
228222
))}
229223
</MultiModelColumn>
230-
<MultiModelColumn gridColumn={4}>
224+
<MultiModelColumn>
231225
{modeledMethods.map((modeledMethod, index) => (
232226
<ModelOutputDropdown
233227
key={index}
@@ -237,7 +231,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
237231
/>
238232
))}
239233
</MultiModelColumn>
240-
<MultiModelColumn gridColumn={5}>
234+
<MultiModelColumn>
241235
{modeledMethods.map((modeledMethod, index) => (
242236
<ModelKindDropdown
243237
key={index}
@@ -248,7 +242,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
248242
))}
249243
</MultiModelColumn>
250244
{viewState.showMultipleModels && (
251-
<MultiModelColumn gridColumn={6}>
245+
<MultiModelColumn>
252246
{modeledMethods.map((_, index) =>
253247
index === modeledMethods.length - 1 ? (
254248
<CodiconRow
@@ -298,7 +292,7 @@ const UnmodelableMethodRow = forwardRef<
298292
ref={ref}
299293
focused={revealedMethodSignature === method.signature}
300294
>
301-
<VSCodeDataGridCell gridColumn={1}>
295+
<DataGridCell>
302296
<ApiOrMethodRow>
303297
<ModelingStatusIndicator status="saved" />
304298
<MethodName {...props.method} />
@@ -310,10 +304,8 @@ const UnmodelableMethodRow = forwardRef<
310304
<ViewLink onClick={jumpToMethod}>View</ViewLink>
311305
<MethodClassifications method={method} />
312306
</ApiOrMethodRow>
313-
</VSCodeDataGridCell>
314-
<VSCodeDataGridCell gridColumn="span 4">
315-
Method already modeled
316-
</VSCodeDataGridCell>
307+
</DataGridCell>
308+
<DataGridCell gridColumn="span 4">Method already modeled</DataGridCell>
317309
</DataGridRow>
318310
);
319311
});

0 commit comments

Comments
 (0)