Skip to content

Commit 8b3106b

Browse files
feat: provideEditorCallback access cell location (#1038)
* feat: provideEditorCallback access cell location * fix data-editor and tests, add documentation * move location into cell * Add storybook case * Add story --------- Co-authored-by: lukasmasuch <[email protected]>
1 parent 20809be commit 8b3106b

File tree

8 files changed

+154
-24
lines changed

8 files changed

+154
-24
lines changed

packages/cells/test/date-picker-cell.test.tsx

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ describe("editor", () => {
5858

5959
it("renders into the dom with correct value", () => {
6060
// @ts-ignore
61-
const Editor = renderer.provideEditor?.(getMockDateCell()).editor;
61+
const Editor = renderer.provideEditor?.({
62+
...getMockDateCell(),
63+
location: [0, 0],
64+
}).editor;
6265
if (Editor === undefined) {
6366
throw new Error("Editor is invalid");
6467
}
@@ -74,10 +77,10 @@ describe("editor", () => {
7477

7578
it.each([["date"], ["time"], ["datetime-local"]])("renders with correct format", (format: string) => {
7679
// @ts-ignore
77-
const Editor = renderer.provideEditor?.(
78-
getMockDateCell({ data: { format: format } } as DatePickerCell)
79-
// @ts-ignore
80-
).editor;
80+
const Editor = renderer.provideEditor?.({
81+
...getMockDateCell({ data: { format: format } } as DatePickerCell),
82+
location: [0, 0],
83+
}).editor;
8184
if (Editor === undefined) {
8285
throw new Error("Editor is invalid");
8386
}
@@ -92,10 +95,10 @@ describe("editor", () => {
9295

9396
it("renders textarea when readonly is true", () => {
9497
// @ts-ignore
95-
const Editor = renderer.provideEditor?.(
96-
getMockDateCell({ readonly: true } as DatePickerCell)
97-
// @ts-ignore
98-
).editor;
98+
const Editor = renderer.provideEditor?.({
99+
...getMockDateCell({ readonly: true } as DatePickerCell),
100+
location: [0, 0],
101+
}).editor;
99102
if (Editor === undefined) {
100103
throw new Error("Editor is invalid");
101104
}
@@ -118,7 +121,10 @@ describe("editor", () => {
118121
};
119122

120123
// @ts-ignore
121-
const Editor = renderer.provideEditor?.(getMockDateCell(extraProps)).editor;
124+
const Editor = renderer.provideEditor?.({
125+
...getMockDateCell(extraProps),
126+
location: [0, 0],
127+
}).editor;
122128
if (Editor === undefined) {
123129
throw new Error("Editor is invalid");
124130
}
@@ -139,7 +145,10 @@ describe("editor", () => {
139145
const valueAsNumber = 100;
140146

141147
// @ts-ignore
142-
const Editor = renderer.provideEditor?.(getMockDateCell()).editor;
148+
const Editor = renderer.provideEditor?.({
149+
...getMockDateCell(),
150+
location: [0, 0],
151+
}).editor;
143152
if (Editor === undefined) {
144153
throw new Error("Editor is invalid");
145154
}
@@ -171,7 +180,10 @@ describe("editor", () => {
171180

172181
it('properly sets new date to undefined when value is ""', async () => {
173182
// @ts-ignore
174-
const Editor = renderer.provideEditor?.(getMockDateCell()).editor;
183+
const Editor = renderer.provideEditor?.({
184+
...getMockDateCell(),
185+
location: [0, 0],
186+
}).editor;
175187
if (Editor === undefined) {
176188
throw new Error("Editor is invalid");
177189
}

packages/cells/test/multi-select-cell.test.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,10 @@ describe("Multi Select Editor", () => {
267267

268268
it("renders into the dom with correct value", async () => {
269269
// @ts-ignore
270-
const Editor = renderer.provideEditor?.(getMockCell()).editor;
270+
const Editor = renderer.provideEditor?.({
271+
...getMockCell(),
272+
location: [0, 0],
273+
}).editor;
271274
if (Editor === undefined) {
272275
throw new Error("Editor is invalid");
273276
}
@@ -285,7 +288,10 @@ describe("Multi Select Editor", () => {
285288
it("allows to select values", async () => {
286289
const mockCell = getMockCell();
287290
// @ts-ignore
288-
const Editor = renderer.provideEditor?.(mockCell).editor;
291+
const Editor = renderer.provideEditor?.({
292+
...mockCell,
293+
location: [0, 0],
294+
}).editor;
289295
if (Editor === undefined) {
290296
throw new Error("Editor is invalid");
291297
}
@@ -317,7 +323,10 @@ describe("Multi Select Editor", () => {
317323
it("is disabled if readonly", async () => {
318324
const mockCell = getMockCell({ readonly: true });
319325
// @ts-ignore
320-
const Editor = renderer.provideEditor?.(mockCell).editor;
326+
const Editor = renderer.provideEditor?.({
327+
...mockCell,
328+
location: [0, 0],
329+
}).editor;
321330
if (Editor === undefined) {
322331
throw new Error("Editor is invalid");
323332
}
@@ -334,7 +343,10 @@ describe("Multi Select Editor", () => {
334343
it("allowDuplicates allows to select values multiple times", async () => {
335344
const mockCell = getMockCell({ data: { allowDuplicates: true } } as any);
336345
// @ts-ignore
337-
const Editor = renderer.provideEditor?.(mockCell).editor;
346+
const Editor = renderer.provideEditor?.({
347+
...mockCell,
348+
location: [0, 0],
349+
}).editor;
338350
if (Editor === undefined) {
339351
throw new Error("Editor is invalid");
340352
}

packages/core/API.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,9 @@ export type ProvideEditorCallbackResult<T extends InnerGridCell> =
597597
| ObjectEditorCallbackResult<T>
598598
| undefined;
599599

600-
export type ProvideEditorCallback<T extends InnerGridCell> = (cell: T) => ProvideEditorCallbackResult<T>;
600+
export type ProvideEditorCallback<T extends InnerGridCell> = (
601+
cell: T & { location?: Item }
602+
) => ProvideEditorCallbackResult<T>;
601603

602604
provideEditor?: ProvideEditorCallback<GridCell>;
603605
```

packages/core/src/data-editor/data-editor.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3023,7 +3023,10 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
30233023
let newVal: InnerGridCell | undefined = undefined;
30243024
if (cellValue.kind === GridCellKind.Custom) {
30253025
const toDelete = getCellRenderer(cellValue);
3026-
const editor = toDelete?.provideEditor?.(cellValue);
3026+
const editor = toDelete?.provideEditor?.({
3027+
...cellValue,
3028+
location: [x - rowMarkerOffset, y],
3029+
});
30273030
if (toDelete?.onDelete !== undefined) {
30283031
newVal = toDelete.onDelete(cellValue);
30293032
} else if (isObjectEditorCallbackResult(editor)) {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import React from "react";
2+
import { DataEditorAll as DataEditor } from "../../data-editor-all.js";
3+
import {
4+
BeautifulWrapper,
5+
Description,
6+
PropName,
7+
defaultProps,
8+
useMockDataGenerator,
9+
} from "../../data-editor/stories/utils.js";
10+
import { SimpleThemeWrapper } from "../../stories/story-utils.js";
11+
import { GridCellKind, type ProvideEditorCallback, type TextCell } from "../../index.js";
12+
13+
export default {
14+
title: "Glide-Data-Grid/DataEditor Demos",
15+
decorators: [
16+
(Story: React.ComponentType) => (
17+
<SimpleThemeWrapper>
18+
<BeautifulWrapper
19+
title="Custom Editors"
20+
description={
21+
<Description>
22+
The <PropName>provideEditor</PropName> callback allows you to provide a custom editor for a
23+
cell. In this example, cells in the first column get a custom editor.
24+
</Description>
25+
}>
26+
<Story />
27+
</BeautifulWrapper>
28+
</SimpleThemeWrapper>
29+
),
30+
],
31+
};
32+
33+
const CustomEditor: React.FC<any> = p => {
34+
const { value, onFinishedEditing } = p;
35+
const [text, setText] = React.useState(value.data);
36+
37+
return (
38+
<div style={{ width: "100%", height: "100%" }}>
39+
Type something:
40+
<input
41+
style={{
42+
width: "100%",
43+
height: "100%",
44+
boxSizing: "border-box",
45+
border: "2px solid #666",
46+
background: "#333",
47+
color: "white",
48+
padding: "0 8px",
49+
}}
50+
value={text}
51+
onChange={e => setText(e.target.value)}
52+
onBlur={() =>
53+
onFinishedEditing({
54+
...value,
55+
data: text,
56+
})
57+
}
58+
/>
59+
</div>
60+
);
61+
};
62+
CustomEditor.displayName = "CustomEditor";
63+
64+
const provideEditor: ProvideEditorCallback<TextCell> = cell => {
65+
// You can get the location of the activated cell via cell.location:
66+
if (cell.location?.[0] === 0) {
67+
// eslint-disable-next-line react/display-name
68+
return p => <CustomEditor {...p} />;
69+
}
70+
return undefined;
71+
};
72+
73+
export const CustomEditors: React.VFC = () => {
74+
const { cols, getCellContent, setCellValue } = useMockDataGenerator(10, false);
75+
76+
return (
77+
<DataEditor
78+
{...defaultProps}
79+
getCellContent={getCellContent}
80+
columns={cols}
81+
rows={20}
82+
onCellEdited={(cell, newValue) => {
83+
if (newValue.kind !== GridCellKind.Text) return;
84+
setCellValue(cell, newValue);
85+
}}
86+
provideEditor={provideEditor as ProvideEditorCallback<any>}
87+
/>
88+
);
89+
};
90+
CustomEditors.displayName = "CustomEditors";

packages/core/src/internal/data-grid-overlay-editor/data-grid-overlay-editor.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,13 @@ const DataGridOverlayEditor: React.FunctionComponent<DataGridOverlayEditorProps>
152152

153153
const [editorProvider, useLabel] = React.useMemo((): [ProvideEditorCallbackResult<GridCell>, boolean] | [] => {
154154
if (isInnerOnlyCell(content)) return [];
155-
const external = provideEditor?.(content);
155+
const cellWithLocation = { ...content, location: cell } as GridCell & {
156+
location: Item;
157+
};
158+
const external = provideEditor?.(cellWithLocation);
156159
if (external !== undefined) return [external, false];
157-
return [getCellRenderer(content)?.provideEditor?.(content), false];
158-
}, [content, getCellRenderer, provideEditor]);
160+
return [getCellRenderer(content)?.provideEditor?.(cellWithLocation), false];
161+
}, [cell, content, getCellRenderer, provideEditor]);
159162

160163
const { ref, style: stayOnScreenStyle } = useStayOnScreen();
161164

packages/core/src/internal/data-grid/data-grid-types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,9 @@ export function isObjectEditorCallbackResult<T extends InnerGridCell>(
422422
}
423423

424424
/** @category Renderers */
425-
export type ProvideEditorCallback<T extends InnerGridCell> = (cell: T) => ProvideEditorCallbackResult<T>;
425+
export type ProvideEditorCallback<T extends InnerGridCell> = (
426+
cell: T & { location?: Item }
427+
) => ProvideEditorCallbackResult<T>;
426428

427429
/** @category Cells */
428430
export type ValidatedGridCell = EditableGridCell & {

packages/core/test/cells.test.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ describe("Image cell", () => {
5959

6060
it("Renders its editor (smoke test)", async () => {
6161
const cell = getImgCell();
62-
const Editor = imageCellRenderer.provideEditor?.(cell);
62+
const Editor = imageCellRenderer.provideEditor?.({
63+
...cell,
64+
location: [0, 0],
65+
});
6366
const target = getMockEditorTarget();
6467

6568
assert(!isObjectEditorCallbackResult(Editor));
@@ -83,7 +86,10 @@ describe("Image cell", () => {
8386

8487
it("Renders a custom editor (smoke test)", async () => {
8588
const cell = getImgCell();
86-
const Editor = imageCellRenderer.provideEditor?.(cell);
89+
const Editor = imageCellRenderer.provideEditor?.({
90+
...cell,
91+
location: [0, 0],
92+
});
8793
assert(Editor !== undefined);
8894
const target = getMockEditorTarget();
8995

0 commit comments

Comments
 (0)