Skip to content

Commit

Permalink
Merge pull request #243 from BSd3v/allowing-filter-functions
Browse files Browse the repository at this point in the history
allowing more functions
  • Loading branch information
alexcjohnson authored Oct 16, 2023
2 parents fff8ba1 + 7352a6c commit 8614b97
Show file tree
Hide file tree
Showing 9 changed files with 512 additions and 2 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@ Links "DE#nnn" prior to version 2.0 point to the Dash Enterprise closed-source D

## [Unreleased]

### Added
- [#243](https://github.com/plotly/dash-ag-grid/pull/243) Added function support for:
- `getContextMenuItems`, `isRowMaster`, `setPopupParent`, `popupParent`, `filter`
- iterate through `csvExportParams` and `defaultCsvExportParams` for functions:
- `getCustomContextBelowRow`, `shouldRowBeSkipped`, `processCellCallback`, `processHeaderCallback`, `processGroupHeaderCallback`, `processRowGroupCallback`

### Fixed
- [#237](https://github.com/plotly/dash-ag-grid/pull/237) Fixed issue with grid components not being passed down to the detail grids
- [#232](https://github.com/plotly/dash-ag-grid/pull/232) Fixed height style to unassign automatically if `domLayout` = 'autoHeight', addresses [#231](https://github.com/plotly/dash-ag-grid/issues/231)

## [2.3.0] - 2023-07-27

Expand Down
4 changes: 3 additions & 1 deletion src/lib/fragments/AgGrid.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,9 @@ export default class DashAgGrid extends Component {
if (!this.state.gridApi) {
return;
}
this.state.gridApi.exportDataAsCsv(csvExportParams);
this.state.gridApi.exportDataAsCsv(
this.convertAllProps(csvExportParams)
);
if (reset) {
this.props.setProps({
exportDataAsCsv: false,
Expand Down
15 changes: 15 additions & 0 deletions src/lib/utils/propCategories.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const GRID_MAYBE_FUNCTIONS = {
// Accessories
getMainMenuItems: 1,
postProcessPopup: 1,
getContextMenuItems: 1,

// Clipboard
processCellForClipboard: 1,
Expand All @@ -55,6 +56,14 @@ export const GRID_MAYBE_FUNCTIONS = {
sendToClipboard: 1,
processDataFromClipboard: 1,

// Exporting
getCustomContentBelowRow: 1,
shouldRowBeSkipped: 1,
processCellCallback: 1,
processHeaderCallback: 1,
processGroupHeaderCallback: 1,
processRowGroupCallback: 1,

// Filtering
isExternalFilterPresent: 1,
doesExternalFilterPass: 1,
Expand All @@ -74,6 +83,7 @@ export const GRID_MAYBE_FUNCTIONS = {

// Miscellaneous
getDocument: 1,
isRowMaster: 1,

// Pagination
paginationNumberFormatter: 1,
Expand Down Expand Up @@ -124,6 +134,8 @@ export const GRID_MAYBE_FUNCTIONS = {
**/
export const GRID_MAYBE_FUNCTIONS_NO_PARAMS = {
frameworkComponents: 1,
setPopupParent: 1,
popupParent: 1,
};

/**
Expand Down Expand Up @@ -151,6 +163,8 @@ export const GRID_COLUMN_CONTAINERS = {
export const GRID_NESTED_FUNCTIONS = {
detailCellRendererParams: 1,
detailGridOptions: 1,
csvExportParams: 1,
defaultCsvExportParams: 1,
};

/**
Expand All @@ -161,6 +175,7 @@ export const GRID_NESTED_FUNCTIONS = {
**/
export const COLUMN_MAYBE_FUNCTIONS_NO_PARAMS = {
cellEditor: 1,
filter: 1,

// Columns: Sort
comparator: 1,
Expand Down
185 changes: 184 additions & 1 deletion tests/assets/dashAgGridFunctions.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,187 @@ function monthToComparableNumber(date) {
const monthNumber = parseInt(date.split('/')[1]);
const dayNumber = parseInt(date.split('/')[0]);
return yearNumber * 10000 + monthNumber * 100 + dayNumber;
}
}

const {useImperativeHandle, useState, useEffect, forwardRef} = React;

// This example was adapted from https://www.ag-grid.com/react-data-grid/component-filter/
// The only differences are:
// - React.createElement instead of JSX
// - setProps, which all Dash components use to report user interactions,
// instead of a plain js event handler
dagfuncs.YearFilter = forwardRef((props, ref) => {
const [year, setYear] = useState('All');

useImperativeHandle(ref, () => {
return {
doesFilterPass(params) {
return params.data.year >= 2010;
},

isFilterActive() {
return year === '2010'
},

// this example isn't using getModel() and setModel(),
// so safe to just leave these empty. don't do this in your code!!!
getModel() {
},

setModel() {
}
}
});

useEffect(() => {
props.filterChangedCallback()
}, [year]);

setProps = ({value}) => {
if (value) {
setYear(value)
}
}

return React.createElement(
window.dash_core_components.RadioItems,
{
options:[
{'label': 'All', 'value': 'All'},
{'label': 'Since 2010', 'value': '2010'},
],
value: year,
setProps
}
)
});

dagfuncs.setBody = () => {
return document.querySelector('body')
}

// cell editor custom component - dmc.Select
dagfuncs.DMC_Select = class {
// gets called once before the renderer is used
init(params) {
// create the cell
this.params = params;

// function for when Dash is trying to send props back to the component / server
var setProps = (props) => {
if (typeof props.value != typeof undefined) {
// updates the value of the editor
this.value = props.value;

// re-enables keyboard event
delete params.colDef.suppressKeyboardEvent;

// tells the grid to stop editing the cell
params.api.stopEditing();

// sets focus back to the grid's previously active cell
this.prevFocus.focus();
}
};
this.eInput = document.createElement('div');

// renders component into the editor element
ReactDOM.render(
React.createElement(window.dash_mantine_components.Select, {
data: params.options,
value: params.value,
setProps,
style: {width: params.column.actualWidth-2, ...params.style},
className: params.className,
clearable: params.clearable,
searchable: params.searchable || true,
creatable: params.creatable,
debounce: params.debounce,
disabled: params.disabled,
filterDataOnExactSearchMatch:
params.filterDataOnExactSearchMatch,
limit: params.limit,
maxDropdownHeight: params.maxDropdownHeight,
nothingFound: params.nothingFound,
placeholder: params.placeholder,
required: params.required,
searchValue: params.searchValue,
shadow: params.shadow,
size: params.size,
styles: params.styles,
switchDirectionOnFlip: params.switchDirectionOnFlip,
variant: params.variant,
}),
this.eInput
);

// allows focus event
this.eInput.tabIndex = '0';

// sets editor value to the value from the cell
this.value = params.value;
}

// gets called once when grid ready to insert the element
getGui() {
return this.eInput;
}

focusChild() {
// needed to delay and allow the component to render
setTimeout(() => {
var inp = this.eInput.getElementsByClassName(
'mantine-Select-input'
)[0];
inp.tabIndex = '1';

// disables keyboard event
this.params.colDef.suppressKeyboardEvent = (params) => {
const gridShouldDoNothing = params.editing;
return gridShouldDoNothing;
};
// shows dropdown options
inp.focus();
}, 100);
}

// focus and select can be done after the gui is attached
afterGuiAttached() {
// stores the active cell
this.prevFocus = document.activeElement;

// adds event listener to trigger event to go into dash component
this.eInput.addEventListener('focus', this.focusChild());

// triggers focus event
this.eInput.focus();
}

// returns the new value after editing
getValue() {
return this.value;
}

// any cleanup we need to be done here
destroy() {
// sets focus back to the grid's previously active cell
this.prevFocus.focus();
}
};

dagfuncs.contextTest = (params) => {
var result = [
{
// custom item
name: 'Alert ' + params.value,
action: () => {
window.alert('Alerting about ' + params.value);
},
cssClasses: ['redFont', 'bold'],
},
'copy',
'separator',
'chartRange',
];
return result;
};
2 changes: 2 additions & 0 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
# pip install -r requirements.txt

dash[ci,dev,testing]>=2.0

dash_mantine_components==0.12.1
68 changes: 68 additions & 0 deletions tests/test_context_menu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import dash_ag_grid as dag
from dash import Dash, html, dcc
from . import utils
import time
import dash_mantine_components

def test_cm001_context_menu(dash_duo):
app = Dash(__name__)

masterColumnDefs = [
{
"headerName": "Country",
"field": "country",
},
{"headerName": "Region", "field": "region"},
{"headerName": "Population", "field": "population"},
]
rowData = [
{
"country": "China",
"region": "Asia",
"population": 1411778724,
},
{
"country": "India",
"region": "Asia",
"population": 1383524897,
},
{
"country": "United States",
"region": "Americas",
"population": 332593407,
},
{
"country": "Indonesia",
"region": "Asia",
"population": 271350000,
},
]

app.layout = html.Div(
[
dag.AgGrid(
id="grid",
enableEnterpriseModules=True,
columnDefs=masterColumnDefs,
rowData=rowData,
columnSize="sizeToFit",
dashGridOptions={
'getContextMenuItems': {'function': 'contextTest(params)'},
'popupParent': {'function': 'setBody()'}
},
),
]
)

dash_duo.start_server(app)

grid = utils.Grid(dash_duo, "grid")

grid.wait_for_cell_text(0, 0, "China")

### testing animations
action = utils.ActionChains(dash_duo.driver)
action.context_click(grid.get_cell(0, 0)).perform()
dash_duo.find_element('body > .ag-popup')

assert dash_duo.find_element('.ag-popup .ag-menu-option-part.ag-menu-option-text').text == 'Alert China'
Loading

0 comments on commit 8614b97

Please sign in to comment.