Skip to content

Commit

Permalink
feat(data frame): Support polars (#1474)
Browse files Browse the repository at this point in the history
Co-authored-by: Barret Schloerke <barret@posit.co>
  • Loading branch information
machow and schloerke committed Jul 9, 2024
1 parent bdb5935 commit 08cd1f0
Show file tree
Hide file tree
Showing 48 changed files with 2,084 additions and 761 deletions.
8 changes: 5 additions & 3 deletions js/data-frame/cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ type CellHtmlValue = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const isShinyHtml = (x: any): x is CellHtmlValue => {
return (
x !== null &&
typeof x !== "string" &&
x !== null && // Note: x === null has `typeof x === "object"`
typeof x === "object" &&
Object.prototype.hasOwnProperty.call(x, "isShinyHtml") &&
x.isShinyHtml === true
);
Expand Down Expand Up @@ -83,6 +83,7 @@ interface TableBodyCellProps {
setData: (fn: (draft: unknown[][]) => void) => void;
cellEditInfo: CellEdit | undefined;
cellStyle: CellStyle | undefined;
cellClassName: string | undefined;
setCellEditMapAtLoc: SetCellEditMapAtLoc;
selection: SelectionSet<string, HTMLTableRowElement>;
}
Expand All @@ -100,6 +101,7 @@ export const TableBodyCell: FC<TableBodyCellProps> = ({
getSortedRowModel,
cellEditInfo,
cellStyle,
cellClassName,
setData,
setCellEditMapAtLoc,
selection,
Expand Down Expand Up @@ -402,7 +404,7 @@ export const TableBodyCell: FC<TableBodyCellProps> = ({
let content: ReactElement | ReturnType<typeof flexRender> | undefined =
undefined;
const cellTitle = errorTitle;
let tableCellClass: string | undefined = undefined;
let tableCellClass: string | undefined = cellClassName;
const addToTableCellClass = (x: string | undefined) => {
if (!x) return;
if (tableCellClass) {
Expand Down
23 changes: 17 additions & 6 deletions js/data-frame/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,27 @@ const ShinyDataGrid: FC<ShinyDataGridProps<unknown>> = ({
columns,
typeHints,
data: tableDataProp,
options: payloadOptions,
options: payloadOptions = {
width: undefined,
height: undefined,
fill: false,
styles: [],
},
} = payload;
const { width, height, fill, filters: withFilters } = payloadOptions;
const {
width,
height,
fill,
filters: withFilters,
styles: initStyleInfos,
} = payloadOptions;

const containerRef = useRef<HTMLDivElement>(null);
const theadRef = useRef<HTMLTableSectionElement>(null);
const tbodyRef = useRef<HTMLTableSectionElement>(null);

const _useStyleInfo = useStyleInfoMap({
initStyleInfos: payloadOptions["styles"],
initStyleInfos: initStyleInfos ?? [],
nrow: tableDataProp.length,
ncol: columns.length,
});
Expand Down Expand Up @@ -486,7 +497,6 @@ const ShinyDataGrid: FC<ShinyDataGridProps<unknown>> = ({
useEffect(() => {
const handleStyles = (event: CustomEvent<{ styles: StyleInfo[] }>) => {
const styles = event.detail.styles;
resetStyleInfos();
setStyleInfos(styles);
};

Expand All @@ -503,7 +513,7 @@ const ShinyDataGrid: FC<ShinyDataGridProps<unknown>> = ({
handleStyles as EventListener
);
};
}, [setStyleInfos]);
}, [id, setStyleInfos]);

useEffect(() => {
if (!id) return;
Expand Down Expand Up @@ -765,7 +775,7 @@ const ShinyDataGrid: FC<ShinyDataGridProps<unknown>> = ({
rowIndex,
columnIndex
);
const cellStyle = getCellStyle(
const { cellStyle, cellClassName } = getCellStyle(
styleInfoMap,
"body",
rowIndex,
Expand All @@ -787,6 +797,7 @@ const ShinyDataGrid: FC<ShinyDataGridProps<unknown>> = ({
getSortedRowModel={table.getSortedRowModel}
cellEditInfo={cellEditInfo}
cellStyle={cellStyle}
cellClassName={cellClassName}
setData={setTableData}
setCellEditMapAtLoc={setCellEditMapAtLoc}
selection={selection}
Expand Down
36 changes: 30 additions & 6 deletions js/data-frame/style-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,30 @@ export const useStyleInfoMap = ({
rowIndex,
columnIndex,
});
const prevObj = draft.get(key) ?? { style: {}, class: undefined };
let newClass: string | undefined = undefined;
if (prevObj.class) {
if (styleInfo.class) {
newClass = `${prevObj.class} ${styleInfo.class}`;
} else {
newClass = prevObj.class;
}
} else {
if (styleInfo.class) {
newClass = styleInfo.class;
} else {
newClass = undefined;
}
}
draft.set(key, {
location: styleInfo.location,
location,
rowIndex,
columnIndex,
style: styleInfo.style,
class: styleInfo.class,
style: {
...prevObj.style,
...styleInfo.style,
},
class: newClass,
});
}
}
Expand All @@ -105,11 +123,13 @@ export const useStyleInfoMap = ({

const setStyleInfos = useCallback(
(styleInfos: StyleInfo[]) => {
// When settings styleInfos, reset all style infos
resetStyleInfos();
for (const styleInfo of styleInfos) {
setStyleInfo(styleInfo);
}
},
[setStyleInfo]
[setStyleInfo, resetStyleInfos]
);

// Init all style infos
Expand Down Expand Up @@ -138,9 +158,13 @@ export const getCellStyle = (
location: StyleLocation,
rowIndex: number,
columnIndex: number
): CellStyle | undefined => {
): { cellStyle: CellStyle | undefined; cellClassName: string | undefined } => {
const key = makeStyleInfoMapKey({ location, rowIndex, columnIndex });
return x.get(key)?.style;
const obj = x.get(key);
return {
cellStyle: obj?.style,
cellClassName: obj?.class,
};
};

// Use a DOM element to convert CSS string to object
Expand Down
2 changes: 1 addition & 1 deletion js/data-frame/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface DataGridOptions {
width?: string;
height?: string;
fill?: boolean;
styles: StyleInfo[];
}

export interface PandasData<TIndex> {
Expand All @@ -33,7 +34,6 @@ export interface PandasData<TIndex> {
options: DataGridOptions;
typeHints?: ReadonlyArray<TypeHint>;
editable?: boolean;
styles: StyleInfo[];
}

export interface PatchInfo {
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ dev =
matplotlib
pandas
pandas-stubs
polars
numpy
shinyswatch>=0.2.4
# Chat() provider types
Expand Down
17 changes: 9 additions & 8 deletions shiny/playwright/controller/_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6255,10 +6255,12 @@ def set_sort(
----------
sort
The sorting configuration to apply. Can be one of the following:
int: Index of the column to sort by (ascending order by default).
ColumnSort: A dictionary specifying a single column sort with 'col' and 'desc' keys.
list[ColumnSort]: A list of dictionaries for multi-column sorting.
None: No sorting applied (not implemented in the current code).
* `int`: Index of the column to sort by (ascending order by default).
* `ColumnSort`: A dictionary specifying a single column sort with 'col' and 'desc' keys.
* `list[int | ColumnSort]`: A list of ints or dictionaries for multi-column sorting.
* `None`: No sorting applied (not implemented in the current code).
If a `desc` values is provided within your `ColumnSort` shaped dictionary, then the direction will be set to that value. If no `desc` value is provided, the column will be sorted in the default sorting order.
timeout
The maximum time to wait for the action to complete. Defaults to `None`.
"""
Expand Down Expand Up @@ -6348,10 +6350,9 @@ def set_filter(
----------
filter
The filter to apply. Can be one of the following:
None: Resets all filters.
str: A string filter (deprecated, use ColumnFilterStr instead).
ColumnFilterStr: A dictionary specifying a string filter with 'col' and 'value' keys.
ColumnFilterNumber: A dictionary specifying a numeric range filter with 'col' and 'value' keys.
* `None`: Resets all filters.
* `ColumnFilterStr`: A dictionary specifying a string filter with 'col' and 'value' keys.
* `ColumnFilterNumber`: A dictionary specifying a numeric range filter with 'col' and 'value' keys.
timeout
The maximum time to wait for the action to complete. Defaults to `None`.
"""
Expand Down
3 changes: 3 additions & 0 deletions shiny/playwright/expect/_expect.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def expect_attribute_to_have_value(
def expect_to_have_class(
loc: Locator,
cls: str,
*,
timeout: Timeout = None,
) -> None:
"""Expect a locator to contain a class value"""
Expand All @@ -56,6 +57,7 @@ def expect_to_have_class(
def expect_not_to_have_class(
loc: Locator,
cls: str,
*,
timeout: Timeout = None,
) -> None:
"""Expect a locator not to contain a class value"""
Expand All @@ -69,6 +71,7 @@ def expect_to_have_style(
css_key: str,
# Str representation for value. Will be put in a regex with `css_key`
css_value: StyleValue,
*,
timeout: Timeout = None,
) -> None:
"""Expect the `style` attribute to have a value. If `value` is `None`, then the style attribute should not exist."""
Expand Down
4 changes: 3 additions & 1 deletion shiny/render/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
DataTable,
data_frame,
)
from ._data_frame_utils import CellSelection, StyleInfo
from ._data_frame_utils import CellSelection
from ._data_frame_utils._types import DataFrameLike, StyleInfo
from ._deprecated import ( # noqa: F401
RenderFunction, # pyright: ignore[reportUnusedImport]
RenderFunctionAsync, # pyright: ignore[reportUnusedImport]
Expand Down Expand Up @@ -47,4 +48,5 @@
"CellValue",
"CellSelection",
"StyleInfo",
"DataFrameLike",
)
Loading

0 comments on commit 08cd1f0

Please sign in to comment.