From cca765734662600a58005ae8d41c83c98e2f6028 Mon Sep 17 00:00:00 2001 From: Bharat Kashyap Date: Thu, 9 Jun 2022 16:06:44 +0200 Subject: [PATCH 01/44] bootstrap docs/landing page site --- docs-old/README.md | 10 + {docs => docs-old}/data/commodity-100.json | 0 {docs => docs-old}/getting-started.md | 0 {docs => docs-old}/google-sheets.md | 0 {docs => docs-old}/images/add-textfield.png | Bin {docs => docs-old}/images/apps-overview.png | Bin .../images/bind-query-state.png | Bin {docs => docs-old}/images/create-api.png | Bin .../images/create-connection.png | Bin {docs => docs-old}/images/create-state.png | Bin {docs => docs-old}/images/editor-overview.png | Bin {docs => docs-old}/images/page-editor.png | Bin {docs => docs-old}/images/release.png | Bin {docs => docs-old}/images/result.png | Bin {docs => docs-old}/images/updated-binding.png | Bin docs-old/package.json | 5 + {docs => docs-old}/setup.md | 0 docs-old/src/modules/constants.js | 1 + docs/README.md | 27 +- docs/babel.config.js | 75 + docs/data/advanced-components/overview.md | 186 + .../accessibility/DensitySelectorGrid.js | 34 + .../accessibility/DensitySelectorGrid.tsx | 34 + .../DensitySelectorGrid.tsx.preview | 6 + .../accessibility/DensitySelectorSmallGrid.js | 35 + .../DensitySelectorSmallGrid.tsx | 35 + .../DensitySelectorSmallGrid.tsx.preview | 7 + .../accessibility/FocusManagement.js | 57 + .../accessibility/FocusManagement.tsx | 55 + .../accessibility/FocusManagement.tsx.preview | 16 + .../data-grid/accessibility/accessibility.md | 133 + .../data/data-grid/aggregation/aggregation.md | 20 + .../column-definition/BasicColumnsGrid.js | 19 + .../column-definition/BasicColumnsGrid.tsx | 19 + .../BasicColumnsGrid.tsx.preview | 10 + .../column-definition/ColumnTypesGrid.js | 146 + .../column-definition/ColumnTypesGrid.tsx | 153 + .../ColumnTypesGrid.tsx.preview | 1 + .../ColumnsSelectorsNoSnap.js | 6 + .../CustomColumnTypesGrid.js | 60 + .../CustomColumnTypesGrid.tsx | 60 + .../CustomColumnTypesGrid.tsx.preview | 8 + .../column-definition/RenderCellGrid.js | 89 + .../column-definition/RenderCellGrid.tsx | 77 + .../RenderCellGrid.tsx.preview | 1 + .../column-definition/RenderExpandCellGrid.js | 188 + .../RenderExpandCellGrid.tsx | 177 + .../RenderExpandCellGrid.tsx.preview | 1 + .../column-definition/ValueFormatterGrid.js | 43 + .../column-definition/ValueFormatterGrid.tsx | 43 + .../column-definition/ValueGetterGrid.js | 33 + .../column-definition/ValueGetterGrid.tsx | 33 + .../ValueGetterGrid.tsx.preview | 1 + .../VisibleColumnsSelectorsNoSnap.js | 6 + .../column-definition/column-definition.md | 245 + .../column-dimensions/ColumnFluidWidthGrid.js | 36 + .../ColumnFluidWidthGrid.tsx | 36 + .../column-dimensions/ColumnMinWidthGrid.js | 21 + .../column-dimensions/ColumnMinWidthGrid.tsx | 21 + .../ColumnMinWidthGrid.tsx.preview | 4 + .../column-dimensions/ColumnSizingGrid.js | 25 + .../column-dimensions/ColumnSizingGrid.tsx | 25 + .../ColumnSizingGrid.tsx.preview | 8 + .../column-dimensions/ColumnWidthGrid.js | 21 + .../column-dimensions/ColumnWidthGrid.tsx | 21 + .../ColumnWidthGrid.tsx.preview | 4 + .../column-dimensions/column-dimensions.md | 68 + .../data-grid/column-groups/column-groups.md | 20 + .../data-grid/column-header/ColumnMenuGrid.js | 17 + .../column-header/ColumnMenuGrid.tsx | 17 + .../column-header/ColumnMenuGrid.tsx.preview | 1 + .../column-header/HeaderColumnsGrid.js | 29 + .../column-header/HeaderColumnsGrid.tsx | 29 + .../HeaderColumnsGrid.tsx.preview | 12 + .../column-header/RenderHeaderGrid.js | 41 + .../column-header/RenderHeaderGrid.tsx | 41 + .../RenderHeaderGrid.tsx.preview | 1 + .../data-grid/column-header/column-header.md | 54 + .../ColumnOrderingDisabledGrid.js | 25 + .../ColumnOrderingDisabledGrid.tsx | 25 + .../ColumnOrderingDisabledGrid.tsx.preview | 8 + .../column-ordering/ColumnOrderingGrid.js | 17 + .../column-ordering/ColumnOrderingGrid.tsx | 17 + .../ColumnOrderingGrid.tsx.preview | 1 + .../column-ordering/column-ordering.md | 31 + .../column-pinning/BasicColumnPinning.js | 134 + .../column-pinning/BasicColumnPinning.tsx | 139 + .../BasicColumnPinning.tsx.preview | 5 + .../column-pinning/ColumnPinningApiNoSnap.js | 7 + .../ColumnPinningWithCheckboxSelection.js | 104 + .../ColumnPinningWithCheckboxSelection.tsx | 106 + ...mnPinningWithCheckboxSelection.tsx.preview | 11 + .../column-pinning/ControlPinnedColumns.js | 98 + .../column-pinning/ControlPinnedColumns.tsx | 106 + .../ControlPinnedColumns.tsx.preview | 11 + .../DisableColumnPinningButtons.js | 117 + .../DisableColumnPinningButtons.tsx | 111 + .../DisableColumnPinningButtons.tsx.preview | 6 + .../column-pinning/column-pinning.md | 87 + .../column-spanning/ColumnSpanningDerived.js | 185 + .../column-spanning/ColumnSpanningDerived.tsx | 194 + .../ColumnSpanningDerived.tsx.preview | 16 + .../column-spanning/ColumnSpanningFunction.js | 118 + .../ColumnSpanningFunction.tsx | 141 + .../ColumnSpanningFunction.tsx.preview | 12 + .../column-spanning/ColumnSpanningNumber.js | 32 + .../column-spanning/ColumnSpanningNumber.tsx | 32 + .../column-spanning/column-spanning.md | 71 + .../column-visibility/ColumnHiding.js | 32 + .../column-visibility/ColumnHiding.tsx | 32 + .../ColumnHiding.tsx.preview | 8 + .../column-visibility/ColumnSelectorGrid.js | 22 + .../column-visibility/ColumnSelectorGrid.tsx | 22 + .../ColumnSelectorGrid.tsx.preview | 6 + .../VisibleColumnsBasicExample.js | 35 + .../VisibleColumnsBasicExample.tsx | 35 + .../VisibleColumnsBasicExample.tsx.preview | 11 + .../VisibleColumnsModelControlled.js | 30 + .../VisibleColumnsModelControlled.tsx | 31 + .../VisibleColumnsModelControlled.tsx.preview | 8 + .../VisibleColumnsModelInitialState.js | 30 + .../VisibleColumnsModelInitialState.tsx | 30 + ...isibleColumnsModelInitialState.tsx.preview | 14 + .../column-visibility/column-visibility.md | 87 + .../data-grid/components/CellWithPopover.js | 63 + .../data-grid/components/CellWithPopover.tsx | 62 + .../data-grid/components/CustomColumnMenu.js | 138 + .../data-grid/components/CustomColumnMenu.tsx | 137 + .../components/CustomEmptyOverlayGrid.js | 96 + .../components/CustomEmptyOverlayGrid.tsx | 96 + .../CustomEmptyOverlayGrid.tsx.preview | 7 + .../data/data-grid/components/CustomFooter.js | 64 + .../data-grid/components/CustomFooter.tsx | 58 + .../components/CustomLoadingOverlayGrid.js | 24 + .../components/CustomLoadingOverlayGrid.tsx | 24 + .../CustomLoadingOverlayGrid.tsx.preview | 7 + .../components/CustomPaginationGrid.js | 48 + .../components/CustomPaginationGrid.tsx | 48 + .../CustomPaginationGrid.tsx.preview | 9 + .../data-grid/components/CustomSortIcons.js | 46 + .../data-grid/components/CustomSortIcons.tsx | 46 + .../components/CustomSortIcons.tsx.preview | 9 + .../data-grid/components/CustomToolbarGrid.js | 40 + .../components/CustomToolbarGrid.tsx | 40 + .../components/CustomToolbarGrid.tsx.preview | 6 + .../data-grid/components/RowContextMenu.js | 122 + .../data-grid/components/RowContextMenu.tsx | 125 + docs/data/data-grid/components/ToolbarGrid.js | 22 + .../data/data-grid/components/ToolbarGrid.tsx | 22 + .../components/ToolbarGrid.tsx.preview | 6 + docs/data/data-grid/components/components.md | 176 + docs/data/data-grid/demo/FullFeaturedDemo.js | 323 + docs/data/data-grid/demo/FullFeaturedDemo.tsx | 344 ++ docs/data/data-grid/demo/demo.md | 14 + .../editing-legacy/BasicEditingGrid.js | 72 + .../editing-legacy/BasicEditingGrid.tsx | 72 + .../BasicEditingGrid.tsx.preview | 1 + .../editing-legacy/BasicRowEditingGrid.js | 72 + .../editing-legacy/BasicRowEditingGrid.tsx | 72 + .../BasicRowEditingGrid.tsx.preview | 1 + .../editing-legacy/CatchEditingEventsGrid.js | 69 + .../editing-legacy/CatchEditingEventsGrid.tsx | 78 + .../CatchEditingEventsGrid.tsx.preview | 8 + .../editing-legacy/CellEditControlGrid.js | 89 + .../editing-legacy/CellEditControlGrid.tsx | 94 + .../CellEditControlGrid.tsx.preview | 11 + .../CellEditServerSidePersistence.js | 124 + .../CellEditServerSidePersistence.tsx | 140 + .../CellEditServerSidePersistence.tsx.preview | 10 + .../ConditionalValidationGrid.js | 97 + .../ConditionalValidationGrid.tsx | 102 + .../ConditionalValidationGrid.tsx.preview | 3 + .../data-grid/editing-legacy/EditApiNoSnap.js | 7 + .../editing-legacy/FullFeaturedCrudGrid.js | 232 + .../editing-legacy/FullFeaturedCrudGrid.tsx | 238 + .../FullFeaturedCrudGrid.tsx.preview | 15 + .../editing-legacy/IsCellEditableGrid.js | 86 + .../editing-legacy/IsCellEditableGrid.tsx | 86 + .../IsCellEditableGrid.tsx.preview | 5 + .../RenderRatingEditCellGrid.js | 110 + .../RenderRatingEditCellGrid.tsx | 83 + .../RenderRatingEditCellGrid.tsx.preview | 1 + .../editing-legacy/RowEditControlGrid.js | 90 + .../editing-legacy/RowEditControlGrid.tsx | 95 + .../RowEditControlGrid.tsx.preview | 12 + .../RowEditServerSidePersistence.js | 130 + .../RowEditServerSidePersistence.tsx | 147 + .../RowEditServerSidePersistence.tsx.preview | 12 + .../editing-legacy/StartEditButtonGrid.js | 173 + .../editing-legacy/StartEditButtonGrid.tsx | 182 + .../ValidateRowModelControlGrid.js | 71 + .../ValidateRowModelControlGrid.tsx | 74 + .../ValidateRowModelControlGrid.tsx.preview | 1 + .../editing-legacy/ValidateServerNameGrid.js | 100 + .../editing-legacy/ValidateServerNameGrid.tsx | 107 + .../ValidateServerNameGrid.tsx.preview | 9 + .../editing-legacy/ValueGetterSetterGrid.js | 41 + .../editing-legacy/ValueGetterSetterGrid.tsx | 46 + .../ValueGetterSetterGrid.tsx.preview | 1 + docs/data/data-grid/editing-legacy/editing.md | 278 + .../editing/AskConfirmationBeforeSave.js | 193 + .../editing/AskConfirmationBeforeSave.tsx | 210 + .../AskConfirmationBeforeSave.tsx.preview | 12 + .../editing/AutoStopEditComponent.js | 93 + .../editing/AutoStopEditComponent.tsx | 82 + .../editing/AutoStopEditComponent.tsx.preview | 5 + .../data-grid/editing/BasicEditingGrid.js | 76 + .../data-grid/editing/BasicEditingGrid.tsx | 76 + .../editing/BasicEditingGrid.tsx.preview | 5 + .../data-grid/editing/BasicRowEditingGrid.js | 77 + .../data-grid/editing/BasicRowEditingGrid.tsx | 77 + .../editing/BasicRowEditingGrid.tsx.preview | 6 + .../editing/ConditionalValidationGrid.js | 99 + .../editing/ConditionalValidationGrid.tsx | 99 + .../ConditionalValidationGrid.tsx.preview | 8 + .../data-grid/editing/CustomEditComponent.js | 100 + .../data-grid/editing/CustomEditComponent.tsx | 83 + .../editing/CustomEditComponent.tsx.preview | 5 + .../editing/DisableStopEditModeOnFocusOut.js | 81 + .../editing/DisableStopEditModeOnFocusOut.tsx | 88 + .../DisableStopEditModeOnFocusOut.tsx.preview | 10 + docs/data/data-grid/editing/EditApiNoSnap.js | 7 + .../data-grid/editing/FullFeaturedCrudGrid.js | 223 + .../editing/FullFeaturedCrudGrid.tsx | 235 + .../editing/FullFeaturedCrudGrid.tsx.preview | 16 + .../data-grid/editing/IsCellEditableGrid.js | 87 + .../data-grid/editing/IsCellEditableGrid.tsx | 87 + .../editing/IsCellEditableGrid.tsx.preview | 6 + .../editing/LinkedFieldsCellEditing.js | 144 + .../editing/LinkedFieldsCellEditing.tsx | 143 + .../LinkedFieldsCellEditing.tsx.preview | 8 + .../editing/LinkedFieldsRowEditing.js | 111 + .../editing/LinkedFieldsRowEditing.tsx | 106 + .../LinkedFieldsRowEditing.tsx.preview | 6 + .../editing/ServerSidePersistence.js | 126 + .../editing/ServerSidePersistence.tsx | 143 + .../data-grid/editing/StartEditButtonGrid.js | 205 + .../data-grid/editing/StartEditButtonGrid.tsx | 218 + .../editing/ValidateServerNameGrid.js | 113 + .../editing/ValidateServerNameGrid.tsx | 120 + .../ValidateServerNameGrid.tsx.preview | 8 + .../editing/ValueParserSetterGrid.js | 49 + .../editing/ValueParserSetterGrid.tsx | 55 + .../editing/ValueParserSetterGrid.tsx.preview | 1 + docs/data/data-grid/editing/editing.md | 550 ++ .../data-grid/events/CatalogOfEventsNoSnap.js | 130 + .../events/DoubleClickWithCtrlToEdit.js | 25 + .../events/DoubleClickWithCtrlToEdit.tsx | 25 + .../DoubleClickWithCtrlToEdit.tsx.preview | 8 + .../data-grid/events/SubscribeToEvents.js | 33 + .../data-grid/events/SubscribeToEvents.tsx | 42 + .../events/SubscribeToEvents.tsx.preview | 8 + docs/data/data-grid/events/events.json | 320 + docs/data/data-grid/events/events.md | 62 + .../data-grid/export/CsvExportApiNoSnap.js | 7 + .../data-grid/export/CsvGetRowsToExport.js | 88 + .../data-grid/export/CsvGetRowsToExport.tsx | 93 + .../export/CsvGetRowsToExport.tsx.preview | 16 + .../export/CsvGetRowsToExportRowGrouping.js | 77 + .../export/CsvGetRowsToExportRowGrouping.tsx | 81 + .../CsvGetRowsToExportRowGrouping.tsx.preview | 10 + docs/data/data-grid/export/CustomExport.js | 107 + docs/data/data-grid/export/CustomExport.tsx | 106 + .../data-grid/export/CustomExport.tsx.preview | 5 + .../data-grid/export/ExcelCustomExport.js | 329 + .../data-grid/export/ExcelCustomExport.tsx | 332 + .../export/ExcelCustomExport.tsx.preview | 10 + docs/data/data-grid/export/ExcelExport.js | 138 + docs/data/data-grid/export/ExcelExport.tsx | 140 + .../data-grid/export/ExcelExport.tsx.preview | 7 + .../data-grid/export/ExcelExportApiNoSnap.js | 7 + .../data-grid/export/ExportCustomToolbar.js | 31 + .../data-grid/export/ExportCustomToolbar.tsx | 31 + .../export/ExportCustomToolbar.tsx.preview | 7 + .../data-grid/export/ExportDefaultToolbar.js | 17 + .../data-grid/export/ExportDefaultToolbar.tsx | 17 + .../export/ExportDefaultToolbar.tsx.preview | 1 + .../data-grid/export/PrintExportApiNoSnap.js | 7 + .../data-grid/export/RemovePrintExport.js | 30 + .../data-grid/export/RemovePrintExport.tsx | 30 + .../export/RemovePrintExport.tsx.preview | 7 + docs/data/data-grid/export/export.md | 356 ++ .../filtering/BasicExampleDataGrid.js | 19 + .../filtering/BasicExampleDataGrid.tsx | 19 + .../BasicExampleDataGrid.tsx.preview | 1 + .../filtering/BasicExampleDataGridPro.js | 19 + .../filtering/BasicExampleDataGridPro.tsx | 19 + .../BasicExampleDataGridPro.tsx.preview | 1 + .../data-grid/filtering/ControlledFilters.js | 36 + .../data-grid/filtering/ControlledFilters.tsx | 36 + .../filtering/ControlledFilters.tsx.preview | 8 + .../filtering/CustomFilterPanelContent.js | 96 + .../filtering/CustomFilterPanelContent.tsx | 97 + .../filtering/CustomFilterPanelPosition.js | 49 + .../filtering/CustomFilterPanelPosition.tsx | 47 + .../CustomFilterPanelPosition.tsx.preview | 14 + .../filtering/CustomInputComponent.js | 125 + .../filtering/CustomInputComponent.tsx | 97 + .../CustomInputComponent.tsx.preview | 14 + .../filtering/CustomMultiValueOperator.js | 180 + .../filtering/CustomMultiValueOperator.tsx | 155 + .../CustomMultiValueOperator.tsx.preview | 6 + .../filtering/CustomRatingOperator.js | 144 + .../filtering/CustomRatingOperator.tsx | 117 + .../filtering/CustomSelectionOperator.js | 110 + .../filtering/CustomSelectionOperator.tsx | 124 + .../CustomSelectionOperator.tsx.preview | 6 + .../DisableFilteringGridAllColumns.js | 19 + .../DisableFilteringGridAllColumns.tsx | 19 + ...DisableFilteringGridAllColumns.tsx.preview | 1 + .../DisableFilteringGridSomeColumns.js | 27 + .../DisableFilteringGridSomeColumns.tsx | 27 + ...isableFilteringGridSomeColumns.tsx.preview | 1 + .../data-grid/filtering/FilterApiNoSnap.js | 7 + .../filtering/FilterSelectorsNoSnap.js | 6 + .../data-grid/filtering/InitialFilters.js | 38 + .../data-grid/filtering/InitialFilters.tsx | 38 + .../filtering/QuickFilteringCustomLogic.js | 70 + .../filtering/QuickFilteringCustomLogic.tsx | 70 + .../QuickFilteringCustomLogic.tsx.preview | 5 + .../filtering/QuickFilteringCustomizedGrid.js | 62 + .../QuickFilteringCustomizedGrid.tsx | 62 + .../QuickFilteringCustomizedGrid.tsx.preview | 13 + .../data-grid/filtering/QuickFilteringGrid.js | 39 + .../filtering/QuickFilteringGrid.tsx | 39 + .../filtering/QuickFilteringGrid.tsx.preview | 14 + .../filtering/RemoveBuiltInOperators.js | 47 + .../filtering/RemoveBuiltInOperators.tsx | 47 + .../RemoveBuiltInOperators.tsx.preview | 12 + .../data-grid/filtering/ServerFilterGrid.js | 28 + .../data-grid/filtering/ServerFilterGrid.tsx | 28 + .../filtering/ServerFilterGrid.tsx.preview | 7 + docs/data/data-grid/filtering/filtering.md | 421 ++ .../data-grid/getting-started/Codesandbox.js | 16 + .../getting-started/CoreV5WithCoreV4.js | 16 + .../getting-started/getting-started.md | 216 + .../data-grid/getting-started/migration-v4.md | 445 ++ docs/data/data-grid/layout/AutoHeightGrid.js | 28 + docs/data/data-grid/layout/AutoHeightGrid.tsx | 28 + .../layout/AutoHeightGrid.tsx.preview | 7 + docs/data/data-grid/layout/FixedSizeGrid.js | 19 + docs/data/data-grid/layout/FixedSizeGrid.tsx | 19 + .../layout/FixedSizeGrid.tsx.preview | 3 + docs/data/data-grid/layout/FlexLayoutGrid.js | 21 + docs/data/data-grid/layout/FlexLayoutGrid.tsx | 21 + .../layout/FlexLayoutGrid.tsx.preview | 5 + docs/data/data-grid/layout/layout.md | 44 + .../localization/CustomLocaleTextGrid.js | 29 + .../localization/CustomLocaleTextGrid.tsx | 29 + .../CustomLocaleTextGrid.tsx.preview | 13 + .../data-grid/localization/localization.md | 135 + .../master-detail/BasicDetailPanels.js | 187 + .../master-detail/BasicDetailPanels.tsx | 182 + .../BasicDetailPanels.tsx.preview | 7 + .../master-detail/ControlMasterDetail.js | 94 + .../master-detail/ControlMasterDetail.tsx | 102 + .../CustomizeDetailPanelToggle.js | 143 + .../CustomizeDetailPanelToggle.tsx | 135 + .../CustomizeDetailPanelToggle.tsx.preview | 7 + .../master-detail/DetailPanelApiNoSnap.js | 7 + .../master-detail/FormDetailPanel.js | 142 + .../master-detail/FormDetailPanel.tsx | 143 + .../master-detail/FormDetailPanel.tsx.preview | 7 + .../master-detail/FullWidthDetailPanel.js | 217 + .../master-detail/FullWidthDetailPanel.tsx | 214 + .../FullWidthDetailPanel.tsx.preview | 8 + .../data-grid/master-detail/master-detail.md | 141 + docs/data/data-grid/overview/DataGridDemo.js | 61 + docs/data/data-grid/overview/DataGridDemo.tsx | 61 + .../overview/DataGridDemo.tsx.preview | 8 + .../data-grid/overview/DataGridPremiumDemo.js | 57 + .../overview/DataGridPremiumDemo.tsx | 56 + .../overview/DataGridPremiumDemo.tsx.preview | 8 + .../data-grid/overview/DataGridProDemo.js | 23 + .../data-grid/overview/DataGridProDemo.tsx | 23 + .../overview/DataGridProDemo.tsx.preview | 7 + docs/data/data-grid/overview/overview.md | 117 + .../pagination/CursorPaginationGrid.js | 73 + .../pagination/CursorPaginationGrid.tsx | 71 + .../CursorPaginationGrid.tsx.preview | 13 + .../data-grid/pagination/PageControlled.js | 26 + .../data-grid/pagination/PageControlled.tsx | 25 + .../pagination/PageControlled.tsx.preview | 8 + .../data-grid/pagination/PageInitialState.js | 32 + .../data-grid/pagination/PageInitialState.tsx | 32 + .../pagination/PageInitialState.tsx.preview | 12 + .../data/data-grid/pagination/PageSizeAuto.js | 45 + .../data-grid/pagination/PageSizeAuto.tsx | 45 + .../pagination/PageSizeControlled.js | 24 + .../pagination/PageSizeControlled.tsx | 23 + .../pagination/PageSizeControlled.tsx.preview | 6 + .../pagination/PageSizeCustomOptions.js | 25 + .../pagination/PageSizeCustomOptions.tsx | 25 + .../PageSizeCustomOptions.tsx.preview | 7 + .../pagination/PageSizeInitialState.js | 25 + .../pagination/PageSizeInitialState.tsx | 25 + .../PageSizeInitialState.tsx.preview | 9 + .../pagination/PaginationApiNoSnap.js | 7 + .../pagination/PaginationSelectorsNoSnap.js | 6 + .../pagination/ServerPaginationGrid.js | 53 + .../pagination/ServerPaginationGrid.tsx | 52 + .../ServerPaginationGrid.tsx.preview | 14 + docs/data/data-grid/pagination/pagination.md | 150 + docs/data/data-grid/pivoting/pivoting.md | 20 + .../row-grouping/RowGroupingApiNoSnap.js | 7 + .../row-grouping/RowGroupingBasicExample.js | 32 + .../row-grouping/RowGroupingBasicExample.tsx | 32 + .../RowGroupingBasicExample.tsx.preview | 6 + .../RowGroupingColDefCanBeGrouped.js | 41 + .../RowGroupingColDefCanBeGrouped.tsx | 42 + .../RowGroupingColDefCanBeGrouped.tsx.preview | 7 + .../row-grouping/RowGroupingControlled.js | 22 + .../row-grouping/RowGroupingControlled.tsx | 20 + .../RowGroupingControlled.tsx.preview | 5 + ...RowGroupingCustomGroupingColDefCallback.js | 78 + ...owGroupingCustomGroupingColDefCallback.tsx | 80 + .../RowGroupingCustomGroupingColDefObject.js | 35 + .../RowGroupingCustomGroupingColDefObject.tsx | 35 + ...pingCustomGroupingColDefObject.tsx.preview | 9 + .../RowGroupingDefaultExpansionDepth.js | 33 + .../RowGroupingDefaultExpansionDepth.tsx | 33 + ...wGroupingDefaultExpansionDepth.tsx.preview | 7 + .../row-grouping/RowGroupingDisabled.js | 13 + .../row-grouping/RowGroupingDisabled.tsx | 13 + .../RowGroupingDisabled.tsx.preview | 1 + .../row-grouping/RowGroupingFullExample.js | 46 + .../row-grouping/RowGroupingFullExample.tsx | 45 + .../RowGroupingFullExample.tsx.preview | 10 + .../RowGroupingGetRowGroupChildren.js | 114 + .../RowGroupingGetRowGroupChildren.tsx | 125 + .../RowGroupingGroupingValueGetter.js | 59 + .../RowGroupingGroupingValueGetter.tsx | 60 + ...RowGroupingGroupingValueGetter.tsx.preview | 6 + .../RowGroupingHideDescendantCount.js | 35 + .../RowGroupingHideDescendantCount.tsx | 35 + ...RowGroupingHideDescendantCount.tsx.preview | 9 + .../row-grouping/RowGroupingInitialState.js | 21 + .../row-grouping/RowGroupingInitialState.tsx | 21 + .../RowGroupingInitialState.tsx.preview | 9 + .../RowGroupingIsGroupExpandedByDefault.js | 36 + .../RowGroupingIsGroupExpandedByDefault.tsx | 37 + ...oupingIsGroupExpandedByDefault.tsx.preview | 7 + .../row-grouping/RowGroupingLeafWithValue.js | 38 + .../row-grouping/RowGroupingLeafWithValue.tsx | 38 + .../RowGroupingLeafWithValue.tsx.preview | 6 + .../RowGroupingMultipleGroupingCol.js | 32 + .../RowGroupingMultipleGroupingCol.tsx | 32 + ...RowGroupingMultipleGroupingCol.tsx.preview | 6 + .../RowGroupingRowsWithMissingGroups.js | 32 + .../RowGroupingRowsWithMissingGroups.tsx | 32 + ...wGroupingRowsWithMissingGroups.tsx.preview | 6 + .../RowGroupingSetChildrenExpansion.js | 50 + .../RowGroupingSetChildrenExpansion.tsx | 50 + ...owGroupingSetChildrenExpansion.tsx.preview | 9 + .../RowGroupingSingleGroupingCol.js | 32 + .../RowGroupingSingleGroupingCol.tsx | 32 + .../RowGroupingSingleGroupingCol.tsx.preview | 6 + ...owGroupingSortingMultipleGroupingColDef.js | 42 + ...wGroupingSortingMultipleGroupingColDef.tsx | 42 + ...gSortingMultipleGroupingColDef.tsx.preview | 16 + .../RowGroupingSortingSingleGroupingColDef.js | 69 + ...RowGroupingSortingSingleGroupingColDef.tsx | 69 + .../RowGroupingUseKeepGroupedColumnsHidden.js | 32 + ...RowGroupingUseKeepGroupedColumnsHidden.tsx | 32 + ...ingUseKeepGroupedColumnsHidden.tsx.preview | 6 + .../data-grid/row-grouping/row-grouping.md | 291 + docs/data/data-grid/rows/DenseHeightGrid.js | 17 + docs/data/data-grid/rows/DenseHeightGrid.tsx | 17 + .../rows/DenseHeightGrid.tsx.preview | 1 + .../data-grid/rows/DynamicRowHeightGrid.js | 64 + .../data-grid/rows/DynamicRowHeightGrid.tsx | 64 + .../rows/DynamicRowHeightGrid.tsx.preview | 10 + docs/data/data-grid/rows/ExpandableCells.js | 107 + docs/data/data-grid/rows/ExpandableCells.tsx | 99 + .../data-grid/rows/InfiniteLoadingGrid.js | 68 + .../data-grid/rows/InfiniteLoadingGrid.tsx | 68 + .../rows/InfiniteLoadingGrid.tsx.preview | 10 + docs/data/data-grid/rows/RowMarginGrid.js | 34 + docs/data/data-grid/rows/RowMarginGrid.tsx | 34 + .../data-grid/rows/RowMarginGrid.tsx.preview | 10 + docs/data/data-grid/rows/RowOrderingGrid.js | 57 + docs/data/data-grid/rows/RowOrderingGrid.tsx | 65 + .../rows/RowOrderingGrid.tsx.preview | 7 + docs/data/data-grid/rows/RowsGrid.js | 16 + docs/data/data-grid/rows/RowsGrid.tsx | 16 + docs/data/data-grid/rows/RowsGrid.tsx.preview | 7 + .../data-grid/rows/RowsGridWithGetRowId.js | 17 + .../data-grid/rows/RowsGridWithGetRowId.tsx | 17 + .../rows/RowsGridWithGetRowId.tsx.preview | 8 + docs/data/data-grid/rows/ThrottledRowsGrid.js | 53 + .../data/data-grid/rows/ThrottledRowsGrid.tsx | 53 + .../rows/ThrottledRowsGrid.tsx.preview | 6 + docs/data/data-grid/rows/UpdateRowsApiRef.js | 86 + docs/data/data-grid/rows/UpdateRowsApiRef.tsx | 86 + docs/data/data-grid/rows/UpdateRowsProp.js | 82 + docs/data/data-grid/rows/UpdateRowsProp.tsx | 82 + .../data-grid/rows/VariableRowHeightGrid.js | 56 + .../data-grid/rows/VariableRowHeightGrid.tsx | 57 + .../rows/VariableRowHeightGrid.tsx.preview | 14 + docs/data/data-grid/rows/rows.md | 292 + .../data-grid/scrolling/ScrollApiNoSnap.js | 7 + .../data-grid/scrolling/ScrollPlayground.js | 110 + .../data-grid/scrolling/ScrollPlayground.tsx | 109 + docs/data/data-grid/scrolling/scrolling.md | 30 + .../selection/CheckboxSelectionGrid.js | 28 + .../selection/CheckboxSelectionGrid.tsx | 28 + .../CheckboxSelectionGrid.tsx.preview | 9 + .../selection/ControlledSelectionGrid.js | 26 + .../selection/ControlledSelectionGrid.tsx | 26 + .../ControlledSelectionGrid.tsx.preview | 8 + ...ControlledSelectionServerPaginationGrid.js | 68 + ...ontrolledSelectionServerPaginationGrid.tsx | 68 + .../selection/DisableClickSelectionGrid.js | 17 + .../selection/DisableClickSelectionGrid.tsx | 17 + .../DisableClickSelectionGrid.tsx.preview | 1 + .../selection/DisableRowSelection.js | 21 + .../selection/DisableRowSelection.tsx | 21 + .../selection/DisableRowSelection.tsx.preview | 5 + .../selection/MultipleRowSelectionGrid.js | 17 + .../selection/MultipleRowSelectionGrid.tsx | 17 + .../MultipleRowSelectionGrid.tsx.preview | 1 + .../data-grid/selection/SelectionApiNoSnap.js | 7 + .../selection/SingleRowSelectionGrid.js | 17 + .../selection/SingleRowSelectionGrid.tsx | 17 + .../SingleRowSelectionGrid.tsx.preview | 1 + docs/data/data-grid/selection/selection.md | 99 + .../data-grid/sorting/BasicExampleDataGrid.js | 19 + .../sorting/BasicExampleDataGrid.tsx | 19 + .../sorting/BasicExampleDataGrid.tsx.preview | 1 + .../sorting/BasicExampleDataGridPro.js | 19 + .../sorting/BasicExampleDataGridPro.tsx | 19 + .../BasicExampleDataGridPro.tsx.preview | 1 + docs/data/data-grid/sorting/ControlledSort.js | 30 + .../data/data-grid/sorting/ControlledSort.tsx | 30 + .../sorting/ControlledSort.tsx.preview | 5 + .../data-grid/sorting/DisableSortingGrid.js | 27 + .../data-grid/sorting/DisableSortingGrid.tsx | 26 + .../sorting/DisableSortingGrid.tsx.preview | 1 + .../sorting/ExtendedSortComparator.js | 78 + .../sorting/ExtendedSortComparator.tsx | 91 + .../ExtendedSortComparator.tsx.preview | 14 + .../sorting/FullyCustomSortComparator.js | 49 + .../sorting/FullyCustomSortComparator.tsx | 50 + .../FullyCustomSortComparator.tsx.preview | 14 + docs/data/data-grid/sorting/InitialSort.js | 31 + docs/data/data-grid/sorting/InitialSort.tsx | 31 + .../data-grid/sorting/InitialSort.tsx.preview | 13 + .../data-grid/sorting/OrderSortingGrid.js | 32 + .../data-grid/sorting/OrderSortingGrid.tsx | 32 + .../sorting/OrderSortingGrid.tsx.preview | 14 + .../sorting/OrderSortingPerColumnGrid.js | 34 + .../sorting/OrderSortingPerColumnGrid.tsx | 34 + .../OrderSortingPerColumnGrid.tsx.preview | 1 + .../data-grid/sorting/ServerSortingGrid.js | 36 + .../data-grid/sorting/ServerSortingGrid.tsx | 36 + .../sorting/ServerSortingGrid.tsx.preview | 7 + .../data-grid/sorting/SortingApiNoSnap.js | 7 + .../sorting/SortingSelectorsNoSnap.js | 6 + docs/data/data-grid/sorting/sorting.md | 158 + docs/data/data-grid/state/DirectSelector.js | 43 + docs/data/data-grid/state/DirectSelector.tsx | 43 + .../state/DirectSelector.tsx.preview | 6 + docs/data/data-grid/state/InitialState.js | 31 + docs/data/data-grid/state/InitialState.tsx | 31 + .../data-grid/state/InitialState.tsx.preview | 15 + .../data-grid/state/RestoreStateApiRef.js | 364 ++ .../data-grid/state/RestoreStateApiRef.tsx | 348 ++ .../state/RestoreStateApiRef.tsx.preview | 7 + .../state/RestoreStateInitialState.js | 81 + .../state/RestoreStateInitialState.tsx | 81 + .../RestoreStateInitialState.tsx.preview | 16 + docs/data/data-grid/state/SelectorsNoSnap.js | 6 + docs/data/data-grid/state/UseGridSelector.js | 49 + docs/data/data-grid/state/UseGridSelector.tsx | 49 + .../state/UseGridSelector.tsx.preview | 10 + docs/data/data-grid/state/state.md | 138 + docs/data/data-grid/style/AntDesignGrid.js | 141 + docs/data/data-grid/style/AntDesignGrid.tsx | 142 + .../data-grid/style/AntDesignGrid.tsx.preview | 9 + docs/data/data-grid/style/StripedGrid.js | 58 + docs/data/data-grid/style/StripedGrid.tsx | 58 + .../data-grid/style/StripedGrid.tsx.preview | 7 + docs/data/data-grid/style/StylingAllCells.js | 47 + docs/data/data-grid/style/StylingAllCells.tsx | 47 + .../style/StylingAllCells.tsx.preview | 10 + docs/data/data-grid/style/StylingCellsGrid.js | 72 + .../data/data-grid/style/StylingCellsGrid.tsx | 72 + .../style/StylingCellsGrid.tsx.preview | 1 + .../data/data-grid/style/StylingHeaderGrid.js | 52 + .../data-grid/style/StylingHeaderGrid.tsx | 52 + .../style/StylingHeaderGrid.tsx.preview | 1 + docs/data/data-grid/style/StylingRowsGrid.js | 70 + docs/data/data-grid/style/StylingRowsGrid.tsx | 70 + .../style/StylingRowsGrid.tsx.preview | 4 + docs/data/data-grid/style/SxProp.js | 27 + docs/data/data-grid/style/SxProp.tsx | 27 + docs/data/data-grid/style/SxProp.tsx.preview | 11 + docs/data/data-grid/style/style.md | 139 + .../tree-data/TreeDataCustomGroupingColumn.js | 257 + .../TreeDataCustomGroupingColumn.tsx | 204 + .../TreeDataCustomGroupingColumn.tsx.preview | 7 + .../TreeDataDisableChildrenFiltering.js | 160 + .../TreeDataDisableChildrenFiltering.tsx | 167 + .../TreeDataDisableChildrenSorting.js | 151 + .../TreeDataDisableChildrenSorting.tsx | 157 + .../tree-data/TreeDataFullExample.js | 17 + .../tree-data/TreeDataFullExample.tsx | 17 + .../tree-data/TreeDataFullExample.tsx.preview | 1 + .../tree-data/TreeDataLazyLoading.js | 365 ++ .../tree-data/TreeDataLazyLoading.tsx | 313 + .../tree-data/TreeDataLazyLoading.tsx.preview | 9 + .../data-grid/tree-data/TreeDataSimple.js | 120 + .../data-grid/tree-data/TreeDataSimple.tsx | 125 + .../tree-data/TreeDataSimple.tsx.preview | 6 + .../data-grid/tree-data/TreeDataWithGap.js | 108 + .../data-grid/tree-data/TreeDataWithGap.tsx | 113 + .../tree-data/TreeDataWithGap.tsx.preview | 6 + docs/data/data-grid/tree-data/tree-data.md | 139 + .../ColumnVirtualizationGrid.js | 45 + .../ColumnVirtualizationGrid.tsx | 55 + .../ColumnVirtualizationGrid.tsx.preview | 1 + .../virtualization/virtualization.md | 53 + .../date-picker/BasicDatePicker.js | 22 + .../date-picker/BasicDatePicker.tsx | 22 + .../date-picker/BasicDatePicker.tsx.preview | 10 + .../date-pickers/date-picker/CustomDay.js | 76 + .../date-pickers/date-picker/CustomDay.tsx | 86 + .../date-picker/CustomDay.tsx.preview | 13 + .../date-pickers/date-picker/CustomInput.js | 27 + .../date-pickers/date-picker/CustomInput.tsx | 27 + .../date-picker/CustomInput.tsx.preview | 15 + .../date-picker/FormPropsDatePickers.js | 35 + .../date-picker/FormPropsDatePickers.tsx | 35 + .../date-pickers/date-picker/HelperText.js | 24 + .../date-pickers/date-picker/HelperText.tsx | 24 + .../date-picker/HelperText.tsx.preview | 12 + .../date-picker/JalaliDatePicker.js | 20 + .../date-picker/JalaliDatePicker.tsx | 20 + .../date-picker/JalaliDatePicker.tsx.preview | 8 + .../date-picker/LocalizedDatePicker.js | 53 + .../date-picker/LocalizedDatePicker.tsx | 53 + .../date-picker/ResponsiveDatePickers.js | 47 + .../date-picker/ResponsiveDatePickers.tsx | 47 + .../date-picker/ServerRequestDatePicker.js | 109 + .../date-picker/ServerRequestDatePicker.tsx | 109 + .../date-picker/StaticDatePickerDemo.js | 23 + .../date-picker/StaticDatePickerDemo.tsx | 23 + .../StaticDatePickerDemo.tsx.preview | 11 + .../date-picker/StaticDatePickerLandscape.js | 25 + .../date-picker/StaticDatePickerLandscape.tsx | 25 + .../StaticDatePickerLandscape.tsx.preview | 12 + .../date-picker/SubComponentsPickers.js | 40 + .../date-picker/SubComponentsPickers.tsx | 40 + .../date-picker/ViewsDatePicker.js | 65 + .../date-picker/ViewsDatePicker.tsx | 65 + .../date-picker/date-picker-pt.md | 99 + .../date-picker/date-picker-zh.md | 99 + .../date-pickers/date-picker/date-picker.md | 102 + .../date-range-picker/BasicDateRangePicker.js | 30 + .../BasicDateRangePicker.tsx | 30 + .../CalendarsDateRangePicker.js | 63 + .../CalendarsDateRangePicker.tsx | 63 + .../CustomDateRangeInputs.js | 26 + .../CustomDateRangeInputs.tsx | 32 + .../CustomDateRangePickerDay.js | 57 + .../CustomDateRangePickerDay.tsx | 63 + .../CustomDateRangePickerDay.tsx.preview | 16 + .../FormPropsDateRangePickers.js | 50 + .../FormPropsDateRangePickers.tsx | 50 + .../MinMaxDateRangePicker.js | 35 + .../MinMaxDateRangePicker.tsx | 35 + .../ResponsiveDateRangePicker.js | 47 + .../ResponsiveDateRangePicker.tsx | 48 + .../StaticDateRangePickerDemo.js | 29 + .../StaticDateRangePickerDemo.tsx | 30 + .../StaticDateRangePickerDemo.tsx.preview | 16 + .../date-range-picker/date-range-picker-pt.md | 66 + .../date-range-picker/date-range-picker-zh.md | 66 + .../date-range-picker/date-range-picker.md | 90 + .../date-time-picker/BasicDateTimePicker.js | 22 + .../date-time-picker/BasicDateTimePicker.tsx | 22 + .../BasicDateTimePicker.tsx.preview | 10 + .../date-time-picker/CustomDateTimePicker.js | 63 + .../date-time-picker/CustomDateTimePicker.tsx | 65 + .../date-time-picker/DateTimeValidation.js | 37 + .../date-time-picker/DateTimeValidation.tsx | 37 + .../FormPropsDateTimePickers.js | 35 + .../FormPropsDateTimePickers.tsx | 35 + .../ResponsiveDateTimePickers.js | 43 + .../ResponsiveDateTimePickers.tsx | 45 + .../StaticDateTimePickerDemo.js | 23 + .../StaticDateTimePickerDemo.tsx | 23 + .../StaticDateTimePickerDemo.tsx.preview | 11 + .../date-time-picker/date-time-picker-pt.md | 59 + .../date-time-picker/date-time-picker-zh.md | 59 + .../date-time-picker/date-time-picker.md | 62 + .../date-time-range-picker.md | 15 + .../getting-started/MaterialUIPickers.js | 50 + .../getting-started/MaterialUIPickers.tsx | 52 + .../getting-started/NativePickers.js | 43 + .../getting-started/NativePickers.tsx | 43 + .../getting-started/getting-started-pt.md | 72 + .../getting-started/getting-started-zh.md | 72 + .../getting-started/getting-started.md | 77 + .../localization/LocalizedDatePicker.js | 61 + .../localization/LocalizedDatePicker.tsx | 61 + .../date-pickers/localization/localization.md | 112 + .../migration-lab/migration-lab.md | 87 + .../time-picker/BasicTimePicker.js | 22 + .../time-picker/BasicTimePicker.tsx | 22 + .../time-picker/BasicTimePicker.tsx.preview | 10 + .../time-picker/FormPropsTimePickers.js | 35 + .../time-picker/FormPropsTimePickers.tsx | 35 + .../time-picker/LocalizedTimePicker.js | 53 + .../time-picker/LocalizedTimePicker.tsx | 53 + .../time-picker/ResponsiveTimePickers.js | 40 + .../time-picker/ResponsiveTimePickers.tsx | 42 + .../time-picker/SecondsTimePicker.js | 42 + .../time-picker/SecondsTimePicker.tsx | 42 + .../time-picker/StaticTimePickerDemo.js | 22 + .../time-picker/StaticTimePickerDemo.tsx | 22 + .../StaticTimePickerDemo.tsx.preview | 10 + .../time-picker/StaticTimePickerLandscape.js | 24 + .../time-picker/StaticTimePickerLandscape.tsx | 24 + .../StaticTimePickerLandscape.tsx.preview | 12 + .../time-picker/SubComponentsTimePickers.js | 14 + .../time-picker/SubComponentsTimePickers.tsx | 14 + .../SubComponentsTimePickers.tsx.preview | 3 + .../time-picker/TimeValidationTimePicker.js | 42 + .../time-picker/TimeValidationTimePicker.tsx | 44 + .../time-picker/time-picker-pt.md | 68 + .../time-picker/time-picker-zh.md | 68 + .../date-pickers/time-picker/time-picker.md | 73 + .../time-range-picker/time-range-picker.md | 15 + docs/data/pages.ts | 195 + docs/next-env.d.ts | 5 + docs/next.config.js | 164 + docs/package.json | 90 +- docs/pages/_app.js | 334 ++ docs/pages/_document.js | 3 + docs/pages/playground.example.tsx | 10 + docs/pages/x/advanced-components/index.js | 11 + .../x/api/data-grid/data-grid-premium.js | 23 + .../x/api/data-grid/data-grid-premium.json | 351 ++ docs/pages/x/api/data-grid/data-grid-pro.js | 23 + docs/pages/x/api/data-grid/data-grid-pro.json | 418 ++ docs/pages/x/api/data-grid/data-grid.js | 23 + docs/pages/x/api/data-grid/data-grid.json | 373 ++ docs/pages/x/api/data-grid/grid-api.js | 7 + docs/pages/x/api/data-grid/grid-api.md | 123 + .../pages/x/api/data-grid/grid-cell-params.js | 7 + .../pages/x/api/data-grid/grid-cell-params.md | 30 + docs/pages/x/api/data-grid/grid-col-def.js | 7 + docs/pages/x/api/data-grid/grid-col-def.md | 56 + .../data-grid/grid-column-pinning-api.json | 27 + .../data-grid/grid-column-pinning-state.js | 7 + .../data-grid/grid-column-pinning-state.md | 16 + .../x/api/data-grid/grid-csv-export-api.json | 16 + .../api/data-grid/grid-csv-export-options.js | 7 + .../api/data-grid/grid-csv-export-options.md | 25 + .../api/data-grid/grid-detail-panel-api.json | 21 + .../grid-disable-virtualization-api.json | 5 + .../x/api/data-grid/grid-editing-api.json | 76 + .../api/data-grid/grid-excel-export-api.json | 16 + .../data-grid/grid-excel-export-options.js | 7 + .../data-grid/grid-excel-export-options.md | 23 + .../x/api/data-grid/grid-filter-api.json | 47 + .../pages/x/api/data-grid/grid-filter-form.js | 23 + .../x/api/data-grid/grid-filter-form.json | 38 + .../pages/x/api/data-grid/grid-filter-item.js | 7 + .../pages/x/api/data-grid/grid-filter-item.md | 22 + .../x/api/data-grid/grid-filter-model.js | 7 + .../x/api/data-grid/grid-filter-model.md | 22 + .../x/api/data-grid/grid-filter-operator.js | 7 + .../x/api/data-grid/grid-filter-operator.md | 23 + .../x/api/data-grid/grid-filter-panel.js | 23 + .../x/api/data-grid/grid-filter-panel.json | 28 + .../x/api/data-grid/grid-new-editing-api.json | 46 + .../x/api/data-grid/grid-old-editing-api.json | 56 + .../x/api/data-grid/grid-pagination-api.json | 16 + .../api/data-grid/grid-print-export-api.json | 11 + .../data-grid/grid-print-export-options.js | 7 + .../data-grid/grid-print-export-options.md | 26 + .../data-grid/grid-row-class-name-params.js | 7 + .../data-grid/grid-row-class-name-params.md | 25 + .../api/data-grid/grid-row-grouping-api.json | 26 + docs/pages/x/api/data-grid/grid-row-params.js | 7 + docs/pages/x/api/data-grid/grid-row-params.md | 22 + .../api/data-grid/grid-row-spacing-params.js | 7 + .../api/data-grid/grid-row-spacing-params.md | 23 + .../x/api/data-grid/grid-scroll-api.json | 21 + .../x/api/data-grid/grid-selection-api.json | 36 + docs/pages/x/api/data-grid/grid-sort-api.json | 46 + docs/pages/x/api/data-grid/index.js | 7 + docs/pages/x/api/data-grid/index.md | 28 + docs/pages/x/api/data-grid/selectors.json | 439 ++ .../date-pickers/calendar-picker-skeleton.js | 23 + .../calendar-picker-skeleton.json | 24 + .../x/api/date-pickers/calendar-picker.js | 23 + .../x/api/date-pickers/calendar-picker.json | 72 + docs/pages/x/api/date-pickers/clock-picker.js | 23 + .../x/api/date-pickers/clock-picker.json | 85 + docs/pages/x/api/date-pickers/date-picker.js | 23 + .../pages/x/api/date-pickers/date-picker.json | 104 + .../api/date-pickers/date-range-picker-day.js | 23 + .../date-pickers/date-range-picker-day.json | 51 + .../x/api/date-pickers/date-range-picker.js | 23 + .../x/api/date-pickers/date-range-picker.json | 105 + .../x/api/date-pickers/date-time-picker.js | 23 + .../x/api/date-pickers/date-time-picker.json | 122 + .../x/api/date-pickers/desktop-date-picker.js | 23 + .../api/date-pickers/desktop-date-picker.json | 99 + .../date-pickers/desktop-date-range-picker.js | 23 + .../desktop-date-range-picker.json | 100 + .../date-pickers/desktop-date-time-picker.js | 23 + .../desktop-date-time-picker.json | 117 + .../x/api/date-pickers/desktop-time-picker.js | 23 + .../api/date-pickers/desktop-time-picker.json | 83 + docs/pages/x/api/date-pickers/index.js | 7 + docs/pages/x/api/date-pickers/index.md | 30 + .../api/date-pickers/localization-provider.js | 23 + .../date-pickers/localization-provider.json | 25 + .../x/api/date-pickers/mobile-date-picker.js | 23 + .../api/date-pickers/mobile-date-picker.json | 97 + .../date-pickers/mobile-date-range-picker.js | 23 + .../mobile-date-range-picker.json | 98 + .../date-pickers/mobile-date-time-picker.js | 23 + .../date-pickers/mobile-date-time-picker.json | 115 + .../x/api/date-pickers/mobile-time-picker.js | 23 + .../api/date-pickers/mobile-time-picker.json | 81 + docs/pages/x/api/date-pickers/month-picker.js | 23 + .../x/api/date-pickers/month-picker.json | 30 + .../api/date-pickers/picker-static-wrapper.js | 23 + .../date-pickers/picker-static-wrapper.json | 24 + docs/pages/x/api/date-pickers/pickers-day.js | 23 + .../pages/x/api/date-pickers/pickers-day.json | 40 + .../x/api/date-pickers/static-date-picker.js | 23 + .../api/date-pickers/static-date-picker.json | 97 + .../date-pickers/static-date-range-picker.js | 23 + .../static-date-range-picker.json | 98 + .../date-pickers/static-date-time-picker.js | 23 + .../date-pickers/static-date-time-picker.json | 115 + .../x/api/date-pickers/static-time-picker.js | 23 + .../api/date-pickers/static-time-picker.json | 81 + docs/pages/x/api/date-pickers/time-picker.js | 23 + .../pages/x/api/date-pickers/time-picker.json | 88 + docs/pages/x/api/date-pickers/year-picker.js | 23 + .../pages/x/api/date-pickers/year-picker.json | 18 + docs/pages/x/react-data-grid/accessibility.js | 11 + docs/pages/x/react-data-grid/aggregation.js | 11 + .../x/react-data-grid/column-definition.js | 11 + .../x/react-data-grid/column-dimensions.js | 11 + docs/pages/x/react-data-grid/column-groups.js | 11 + docs/pages/x/react-data-grid/column-header.js | 11 + .../x/react-data-grid/column-ordering.js | 11 + .../pages/x/react-data-grid/column-pinning.js | 11 + .../x/react-data-grid/column-spanning.js | 11 + .../x/react-data-grid/column-visibility.js | 11 + docs/pages/x/react-data-grid/components.js | 11 + docs/pages/x/react-data-grid/demo.js | 7 + .../pages/x/react-data-grid/editing-legacy.js | 11 + docs/pages/x/react-data-grid/editing.js | 7 + docs/pages/x/react-data-grid/events.js | 7 + docs/pages/x/react-data-grid/export.js | 7 + docs/pages/x/react-data-grid/filtering.js | 11 + .../x/react-data-grid/getting-started.js | 11 + docs/pages/x/react-data-grid/index.js | 11 + docs/pages/x/react-data-grid/layout.js | 7 + docs/pages/x/react-data-grid/localization.js | 11 + docs/pages/x/react-data-grid/master-detail.js | 11 + docs/pages/x/react-data-grid/migration-v4.js | 11 + docs/pages/x/react-data-grid/pagination.js | 11 + docs/pages/x/react-data-grid/pivoting.js | 11 + docs/pages/x/react-data-grid/row-grouping.js | 11 + docs/pages/x/react-data-grid/rows.js | 7 + docs/pages/x/react-data-grid/scrolling.js | 11 + docs/pages/x/react-data-grid/selection.js | 11 + docs/pages/x/react-data-grid/sorting.js | 7 + docs/pages/x/react-data-grid/state.js | 7 + docs/pages/x/react-data-grid/style.js | 7 + docs/pages/x/react-data-grid/tree-data.js | 11 + .../pages/x/react-data-grid/virtualization.js | 11 + .../pages/x/react-date-pickers/date-picker.js | 11 + .../x/react-date-pickers/date-range-picker.js | 11 + .../x/react-date-pickers/date-time-picker.js | 11 + .../date-time-range-picker.js | 11 + .../x/react-date-pickers/getting-started.js | 11 + .../x/react-date-pickers/localization.js | 11 + .../x/react-date-pickers/migration-lab.js | 11 + .../pages/x/react-date-pickers/time-picker.js | 11 + .../x/react-date-pickers/time-range-picker.js | 11 + docs/public/_headers | 19 + docs/public/_redirects | 24 + docs/public/favicon.ico | Bin 0 -> 15406 bytes docs/public/robots.txt | 2 + docs/public/static/ads-in-house/figma.png | Bin 0 -> 2044 bytes .../static/ads-in-house/scaffoldhub.png | Bin 0 -> 5547 bytes docs/public/static/ads-in-house/themes-2.jpg | Bin 0 -> 18168 bytes docs/public/static/ads-in-house/themes.png | Bin 0 -> 34542 bytes docs/public/static/ads-in-house/tidelift.png | Bin 0 -> 11308 bytes docs/public/static/favicon.ico | Bin 0 -> 619 bytes docs/public/static/fonts/IBMPlexSans-Bold.ttf | Bin 0 -> 180672 bytes .../public/static/fonts/IBMPlexSans-Bold.woff | Bin 0 -> 78220 bytes .../static/fonts/IBMPlexSans-Bold.woff2 | Bin 0 -> 56592 bytes .../static/fonts/IBMPlexSans-Medium.ttf | Bin 0 -> 182088 bytes .../static/fonts/IBMPlexSans-Medium.woff | Bin 0 -> 82888 bytes .../static/fonts/IBMPlexSans-Medium.woff2 | Bin 0 -> 59912 bytes .../static/fonts/IBMPlexSans-Regular.ttf | Bin 0 -> 180452 bytes .../static/fonts/IBMPlexSans-Regular.woff | Bin 0 -> 78672 bytes .../static/fonts/IBMPlexSans-Regular.woff2 | Bin 0 -> 56516 bytes .../static/fonts/IBMPlexSans-SemiBold.ttf | Bin 0 -> 182176 bytes .../static/fonts/IBMPlexSans-SemiBold.woff | Bin 0 -> 83148 bytes .../static/fonts/IBMPlexSans-SemiBold.woff2 | Bin 0 -> 60384 bytes .../fonts/PlusJakartaSans-Bold-subset.woff2 | Bin 0 -> 6764 bytes .../PlusJakartaSans-ExtraBold-subset.woff2 | Bin 0 -> 6632 bytes docs/public/static/icons/180x180.png | Bin 0 -> 3439 bytes docs/public/static/icons/192x192.png | Bin 0 -> 3763 bytes docs/public/static/icons/256x256.png | Bin 0 -> 5305 bytes docs/public/static/icons/384x384.png | Bin 0 -> 8594 bytes docs/public/static/icons/48x48.png | Bin 0 -> 924 bytes docs/public/static/icons/512x512.png | Bin 0 -> 12683 bytes docs/public/static/icons/96x96.png | Bin 0 -> 1862 bytes docs/public/static/logo.png | Bin 0 -> 16799 bytes docs/public/static/logo.svg | 1 + docs/public/static/logo_raw.svg | 1 + docs/public/static/manifest.json | 34 + docs/public/static/sponsors/doit-square.svg | 3 + docs/public/static/sponsors/doit.svg | 3 + docs/public/static/sponsors/octopus-dark.svg | 4 + docs/public/static/sponsors/octopus-light.svg | 4 + docs/public/static/sponsors/octopus.svg | 3 + docs/public/static/sponsors/tidelift.svg | 9 + docs/public/static/styles/prism-okaidia.css | 137 + .../static/x/commercial-header-icon-dark.png | Bin 0 -> 66417 bytes .../static/x/commercial-header-icon-light.png | Bin 0 -> 63512 bytes docs/public/static/x/premium.svg | 1 + docs/public/static/x/pro.svg | 1 + docs/public/static/x/watermark-dark.png | Bin 0 -> 37505 bytes docs/public/static/x/watermark-light.png | Bin 0 -> 36119 bytes docs/scripts/api/buildApi.ts | 61 + .../api/buildComponentsDocumentation.ts | 560 ++ docs/scripts/api/buildExportsDocumentation.ts | 38 + .../api/buildGridEventsDocumentation.ts | 69 + .../api/buildGridSelectorsDocumentation.ts | 95 + .../api/buildInterfacesDocumentation.ts | 360 ++ docs/scripts/api/utils.ts | 129 + docs/scripts/buildIcons.js | 33 + docs/scripts/buildServiceWorker.js | 25 + docs/scripts/formattedTSDemos.js | 224 + docs/scripts/generateProptypes.ts | 134 + docs/scripts/getTypeScriptProjects.ts | 257 + docs/scripts/helpers.js | 21 + docs/scripts/i18n.js | 39 + docs/scripts/tsconfig.json | 19 + docs/scripts/utils.ts | 16 + docs/src/modules/components/ApiDocs.js | 73 + docs/src/modules/components/ApiPage.js | 435 ++ docs/src/modules/components/SelectorsDocs.js | 140 + docs/src/modules/constants.js | 2 +- .../modules/utils/babel-plugin-jsx-preview.js | 102 + docs/src/modules/utils/find.js | 145 + docs/src/modules/utils/findPages.js | 6 + docs/src/sw.js | 30 + .../data-grid/data-grid-premium-pt.json | 574 ++ .../data-grid/data-grid-premium-zh.json | 574 ++ .../api-docs/data-grid/data-grid-premium.json | 574 ++ .../api-docs/data-grid/data-grid-pro-pt.json | 620 ++ .../api-docs/data-grid/data-grid-pro-zh.json | 620 ++ .../api-docs/data-grid/data-grid-pro.json | 620 ++ .../api-docs/data-grid/data-grid-pt.json | 591 ++ .../api-docs/data-grid/data-grid-zh.json | 591 ++ .../api-docs/data-grid/data-grid.json | 591 ++ .../data-grid/grid-filter-form-pt.json | 23 + .../data-grid/grid-filter-form-zh.json | 23 + .../api-docs/data-grid/grid-filter-form.json | 23 + .../data-grid/grid-filter-panel-pt.json | 11 + .../data-grid/grid-filter-panel-zh.json | 11 + .../api-docs/data-grid/grid-filter-panel.json | 11 + .../date-pickers/calendar-picker-pt.json | 41 + .../calendar-picker-skeleton-pt.json | 16 + .../calendar-picker-skeleton-zh.json | 16 + .../calendar-picker-skeleton.json | 16 + .../date-pickers/calendar-picker-zh.json | 41 + .../date-pickers/calendar-picker.json | 41 + .../date-pickers/clock-picker-pt.json | 38 + .../date-pickers/clock-picker-zh.json | 38 + .../api-docs/date-pickers/clock-picker.json | 38 + .../api-docs/date-pickers/date-picker-pt.json | 64 + .../api-docs/date-pickers/date-picker-zh.json | 64 + .../api-docs/date-pickers/date-picker.json | 64 + .../date-range-picker-day-pt.json | 75 + .../date-range-picker-day-zh.json | 75 + .../date-pickers/date-range-picker-day.json | 75 + .../date-pickers/date-range-picker-pt.json | 62 + .../date-pickers/date-range-picker-zh.json | 62 + .../date-pickers/date-range-picker.json | 62 + .../date-pickers/date-time-picker-pt.json | 77 + .../date-pickers/date-time-picker-zh.json | 77 + .../date-pickers/date-time-picker.json | 77 + .../date-pickers/desktop-date-picker-pt.json | 62 + .../date-pickers/desktop-date-picker-zh.json | 62 + .../date-pickers/desktop-date-picker.json | 62 + .../desktop-date-range-picker-pt.json | 60 + .../desktop-date-range-picker-zh.json | 60 + .../desktop-date-range-picker.json | 60 + .../desktop-date-time-picker-pt.json | 75 + .../desktop-date-time-picker-zh.json | 75 + .../desktop-date-time-picker.json | 75 + .../date-pickers/desktop-time-picker-pt.json | 49 + .../date-pickers/desktop-time-picker-zh.json | 49 + .../date-pickers/desktop-time-picker.json | 49 + .../localization-provider-pt.json | 13 + .../localization-provider-zh.json | 13 + .../date-pickers/localization-provider.json | 13 + .../date-pickers/mobile-date-picker-pt.json | 60 + .../date-pickers/mobile-date-picker-zh.json | 60 + .../date-pickers/mobile-date-picker.json | 60 + .../mobile-date-range-picker-pt.json | 58 + .../mobile-date-range-picker-zh.json | 58 + .../mobile-date-range-picker.json | 58 + .../mobile-date-time-picker-pt.json | 73 + .../mobile-date-time-picker-zh.json | 73 + .../date-pickers/mobile-date-time-picker.json | 73 + .../date-pickers/mobile-time-picker-pt.json | 47 + .../date-pickers/mobile-time-picker-zh.json | 47 + .../date-pickers/mobile-time-picker.json | 47 + .../date-pickers/month-picker-pt.json | 19 + .../date-pickers/month-picker-zh.json | 19 + .../api-docs/date-pickers/month-picker.json | 19 + .../picker-static-wrapper-pt.json | 17 + .../picker-static-wrapper-zh.json | 17 + .../date-pickers/picker-static-wrapper.json | 17 + .../api-docs/date-pickers/pickers-day-pt.json | 49 + .../api-docs/date-pickers/pickers-day-zh.json | 49 + .../api-docs/date-pickers/pickers-day.json | 49 + .../date-pickers/static-date-picker-pt.json | 57 + .../date-pickers/static-date-picker-zh.json | 57 + .../date-pickers/static-date-picker.json | 57 + .../static-date-range-picker-pt.json | 55 + .../static-date-range-picker-zh.json | 55 + .../static-date-range-picker.json | 55 + .../static-date-time-picker-pt.json | 70 + .../static-date-time-picker-zh.json | 70 + .../date-pickers/static-date-time-picker.json | 70 + .../date-pickers/static-time-picker-pt.json | 44 + .../date-pickers/static-time-picker-zh.json | 44 + .../date-pickers/static-time-picker.json | 44 + .../api-docs/date-pickers/time-picker-pt.json | 51 + .../api-docs/date-pickers/time-picker-zh.json | 51 + .../api-docs/date-pickers/time-picker.json | 51 + .../api-docs/date-pickers/year-picker-pt.json | 12 + .../api-docs/date-pickers/year-picker-zh.json | 12 + .../api-docs/date-pickers/year-picker.json | 12 + docs/translations/translations-zh.json | 242 + docs/translations/translations.json | 243 + docs/tsconfig.json | 26 + docs/tslint.json | 8 + package.json | 2 + yarn.lock | 5316 ++++++++++++----- 1059 files changed, 63307 insertions(+), 1645 deletions(-) create mode 100644 docs-old/README.md rename {docs => docs-old}/data/commodity-100.json (100%) rename {docs => docs-old}/getting-started.md (100%) rename {docs => docs-old}/google-sheets.md (100%) rename {docs => docs-old}/images/add-textfield.png (100%) rename {docs => docs-old}/images/apps-overview.png (100%) rename {docs => docs-old}/images/bind-query-state.png (100%) rename {docs => docs-old}/images/create-api.png (100%) rename {docs => docs-old}/images/create-connection.png (100%) rename {docs => docs-old}/images/create-state.png (100%) rename {docs => docs-old}/images/editor-overview.png (100%) rename {docs => docs-old}/images/page-editor.png (100%) rename {docs => docs-old}/images/release.png (100%) rename {docs => docs-old}/images/result.png (100%) rename {docs => docs-old}/images/updated-binding.png (100%) create mode 100644 docs-old/package.json rename {docs => docs-old}/setup.md (100%) create mode 100644 docs-old/src/modules/constants.js create mode 100644 docs/babel.config.js create mode 100644 docs/data/advanced-components/overview.md create mode 100644 docs/data/data-grid/accessibility/DensitySelectorGrid.js create mode 100644 docs/data/data-grid/accessibility/DensitySelectorGrid.tsx create mode 100644 docs/data/data-grid/accessibility/DensitySelectorGrid.tsx.preview create mode 100644 docs/data/data-grid/accessibility/DensitySelectorSmallGrid.js create mode 100644 docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx create mode 100644 docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx.preview create mode 100644 docs/data/data-grid/accessibility/FocusManagement.js create mode 100644 docs/data/data-grid/accessibility/FocusManagement.tsx create mode 100644 docs/data/data-grid/accessibility/FocusManagement.tsx.preview create mode 100644 docs/data/data-grid/accessibility/accessibility.md create mode 100644 docs/data/data-grid/aggregation/aggregation.md create mode 100644 docs/data/data-grid/column-definition/BasicColumnsGrid.js create mode 100644 docs/data/data-grid/column-definition/BasicColumnsGrid.tsx create mode 100644 docs/data/data-grid/column-definition/BasicColumnsGrid.tsx.preview create mode 100644 docs/data/data-grid/column-definition/ColumnTypesGrid.js create mode 100644 docs/data/data-grid/column-definition/ColumnTypesGrid.tsx create mode 100644 docs/data/data-grid/column-definition/ColumnTypesGrid.tsx.preview create mode 100644 docs/data/data-grid/column-definition/ColumnsSelectorsNoSnap.js create mode 100644 docs/data/data-grid/column-definition/CustomColumnTypesGrid.js create mode 100644 docs/data/data-grid/column-definition/CustomColumnTypesGrid.tsx create mode 100644 docs/data/data-grid/column-definition/CustomColumnTypesGrid.tsx.preview create mode 100644 docs/data/data-grid/column-definition/RenderCellGrid.js create mode 100644 docs/data/data-grid/column-definition/RenderCellGrid.tsx create mode 100644 docs/data/data-grid/column-definition/RenderCellGrid.tsx.preview create mode 100644 docs/data/data-grid/column-definition/RenderExpandCellGrid.js create mode 100644 docs/data/data-grid/column-definition/RenderExpandCellGrid.tsx create mode 100644 docs/data/data-grid/column-definition/RenderExpandCellGrid.tsx.preview create mode 100644 docs/data/data-grid/column-definition/ValueFormatterGrid.js create mode 100644 docs/data/data-grid/column-definition/ValueFormatterGrid.tsx create mode 100644 docs/data/data-grid/column-definition/ValueGetterGrid.js create mode 100644 docs/data/data-grid/column-definition/ValueGetterGrid.tsx create mode 100644 docs/data/data-grid/column-definition/ValueGetterGrid.tsx.preview create mode 100644 docs/data/data-grid/column-definition/VisibleColumnsSelectorsNoSnap.js create mode 100644 docs/data/data-grid/column-definition/column-definition.md create mode 100644 docs/data/data-grid/column-dimensions/ColumnFluidWidthGrid.js create mode 100644 docs/data/data-grid/column-dimensions/ColumnFluidWidthGrid.tsx create mode 100644 docs/data/data-grid/column-dimensions/ColumnMinWidthGrid.js create mode 100644 docs/data/data-grid/column-dimensions/ColumnMinWidthGrid.tsx create mode 100644 docs/data/data-grid/column-dimensions/ColumnMinWidthGrid.tsx.preview create mode 100644 docs/data/data-grid/column-dimensions/ColumnSizingGrid.js create mode 100644 docs/data/data-grid/column-dimensions/ColumnSizingGrid.tsx create mode 100644 docs/data/data-grid/column-dimensions/ColumnSizingGrid.tsx.preview create mode 100644 docs/data/data-grid/column-dimensions/ColumnWidthGrid.js create mode 100644 docs/data/data-grid/column-dimensions/ColumnWidthGrid.tsx create mode 100644 docs/data/data-grid/column-dimensions/ColumnWidthGrid.tsx.preview create mode 100644 docs/data/data-grid/column-dimensions/column-dimensions.md create mode 100644 docs/data/data-grid/column-groups/column-groups.md create mode 100644 docs/data/data-grid/column-header/ColumnMenuGrid.js create mode 100644 docs/data/data-grid/column-header/ColumnMenuGrid.tsx create mode 100644 docs/data/data-grid/column-header/ColumnMenuGrid.tsx.preview create mode 100644 docs/data/data-grid/column-header/HeaderColumnsGrid.js create mode 100644 docs/data/data-grid/column-header/HeaderColumnsGrid.tsx create mode 100644 docs/data/data-grid/column-header/HeaderColumnsGrid.tsx.preview create mode 100644 docs/data/data-grid/column-header/RenderHeaderGrid.js create mode 100644 docs/data/data-grid/column-header/RenderHeaderGrid.tsx create mode 100644 docs/data/data-grid/column-header/RenderHeaderGrid.tsx.preview create mode 100644 docs/data/data-grid/column-header/column-header.md create mode 100644 docs/data/data-grid/column-ordering/ColumnOrderingDisabledGrid.js create mode 100644 docs/data/data-grid/column-ordering/ColumnOrderingDisabledGrid.tsx create mode 100644 docs/data/data-grid/column-ordering/ColumnOrderingDisabledGrid.tsx.preview create mode 100644 docs/data/data-grid/column-ordering/ColumnOrderingGrid.js create mode 100644 docs/data/data-grid/column-ordering/ColumnOrderingGrid.tsx create mode 100644 docs/data/data-grid/column-ordering/ColumnOrderingGrid.tsx.preview create mode 100644 docs/data/data-grid/column-ordering/column-ordering.md create mode 100644 docs/data/data-grid/column-pinning/BasicColumnPinning.js create mode 100644 docs/data/data-grid/column-pinning/BasicColumnPinning.tsx create mode 100644 docs/data/data-grid/column-pinning/BasicColumnPinning.tsx.preview create mode 100644 docs/data/data-grid/column-pinning/ColumnPinningApiNoSnap.js create mode 100644 docs/data/data-grid/column-pinning/ColumnPinningWithCheckboxSelection.js create mode 100644 docs/data/data-grid/column-pinning/ColumnPinningWithCheckboxSelection.tsx create mode 100644 docs/data/data-grid/column-pinning/ColumnPinningWithCheckboxSelection.tsx.preview create mode 100644 docs/data/data-grid/column-pinning/ControlPinnedColumns.js create mode 100644 docs/data/data-grid/column-pinning/ControlPinnedColumns.tsx create mode 100644 docs/data/data-grid/column-pinning/ControlPinnedColumns.tsx.preview create mode 100644 docs/data/data-grid/column-pinning/DisableColumnPinningButtons.js create mode 100644 docs/data/data-grid/column-pinning/DisableColumnPinningButtons.tsx create mode 100644 docs/data/data-grid/column-pinning/DisableColumnPinningButtons.tsx.preview create mode 100644 docs/data/data-grid/column-pinning/column-pinning.md create mode 100644 docs/data/data-grid/column-spanning/ColumnSpanningDerived.js create mode 100644 docs/data/data-grid/column-spanning/ColumnSpanningDerived.tsx create mode 100644 docs/data/data-grid/column-spanning/ColumnSpanningDerived.tsx.preview create mode 100644 docs/data/data-grid/column-spanning/ColumnSpanningFunction.js create mode 100644 docs/data/data-grid/column-spanning/ColumnSpanningFunction.tsx create mode 100644 docs/data/data-grid/column-spanning/ColumnSpanningFunction.tsx.preview create mode 100644 docs/data/data-grid/column-spanning/ColumnSpanningNumber.js create mode 100644 docs/data/data-grid/column-spanning/ColumnSpanningNumber.tsx create mode 100644 docs/data/data-grid/column-spanning/column-spanning.md create mode 100644 docs/data/data-grid/column-visibility/ColumnHiding.js create mode 100644 docs/data/data-grid/column-visibility/ColumnHiding.tsx create mode 100644 docs/data/data-grid/column-visibility/ColumnHiding.tsx.preview create mode 100644 docs/data/data-grid/column-visibility/ColumnSelectorGrid.js create mode 100644 docs/data/data-grid/column-visibility/ColumnSelectorGrid.tsx create mode 100644 docs/data/data-grid/column-visibility/ColumnSelectorGrid.tsx.preview create mode 100644 docs/data/data-grid/column-visibility/VisibleColumnsBasicExample.js create mode 100644 docs/data/data-grid/column-visibility/VisibleColumnsBasicExample.tsx create mode 100644 docs/data/data-grid/column-visibility/VisibleColumnsBasicExample.tsx.preview create mode 100644 docs/data/data-grid/column-visibility/VisibleColumnsModelControlled.js create mode 100644 docs/data/data-grid/column-visibility/VisibleColumnsModelControlled.tsx create mode 100644 docs/data/data-grid/column-visibility/VisibleColumnsModelControlled.tsx.preview create mode 100644 docs/data/data-grid/column-visibility/VisibleColumnsModelInitialState.js create mode 100644 docs/data/data-grid/column-visibility/VisibleColumnsModelInitialState.tsx create mode 100644 docs/data/data-grid/column-visibility/VisibleColumnsModelInitialState.tsx.preview create mode 100644 docs/data/data-grid/column-visibility/column-visibility.md create mode 100644 docs/data/data-grid/components/CellWithPopover.js create mode 100644 docs/data/data-grid/components/CellWithPopover.tsx create mode 100644 docs/data/data-grid/components/CustomColumnMenu.js create mode 100644 docs/data/data-grid/components/CustomColumnMenu.tsx create mode 100644 docs/data/data-grid/components/CustomEmptyOverlayGrid.js create mode 100644 docs/data/data-grid/components/CustomEmptyOverlayGrid.tsx create mode 100644 docs/data/data-grid/components/CustomEmptyOverlayGrid.tsx.preview create mode 100644 docs/data/data-grid/components/CustomFooter.js create mode 100644 docs/data/data-grid/components/CustomFooter.tsx create mode 100644 docs/data/data-grid/components/CustomLoadingOverlayGrid.js create mode 100644 docs/data/data-grid/components/CustomLoadingOverlayGrid.tsx create mode 100644 docs/data/data-grid/components/CustomLoadingOverlayGrid.tsx.preview create mode 100644 docs/data/data-grid/components/CustomPaginationGrid.js create mode 100644 docs/data/data-grid/components/CustomPaginationGrid.tsx create mode 100644 docs/data/data-grid/components/CustomPaginationGrid.tsx.preview create mode 100644 docs/data/data-grid/components/CustomSortIcons.js create mode 100644 docs/data/data-grid/components/CustomSortIcons.tsx create mode 100644 docs/data/data-grid/components/CustomSortIcons.tsx.preview create mode 100644 docs/data/data-grid/components/CustomToolbarGrid.js create mode 100644 docs/data/data-grid/components/CustomToolbarGrid.tsx create mode 100644 docs/data/data-grid/components/CustomToolbarGrid.tsx.preview create mode 100644 docs/data/data-grid/components/RowContextMenu.js create mode 100644 docs/data/data-grid/components/RowContextMenu.tsx create mode 100644 docs/data/data-grid/components/ToolbarGrid.js create mode 100644 docs/data/data-grid/components/ToolbarGrid.tsx create mode 100644 docs/data/data-grid/components/ToolbarGrid.tsx.preview create mode 100644 docs/data/data-grid/components/components.md create mode 100644 docs/data/data-grid/demo/FullFeaturedDemo.js create mode 100644 docs/data/data-grid/demo/FullFeaturedDemo.tsx create mode 100644 docs/data/data-grid/demo/demo.md create mode 100644 docs/data/data-grid/editing-legacy/BasicEditingGrid.js create mode 100644 docs/data/data-grid/editing-legacy/BasicEditingGrid.tsx create mode 100644 docs/data/data-grid/editing-legacy/BasicEditingGrid.tsx.preview create mode 100644 docs/data/data-grid/editing-legacy/BasicRowEditingGrid.js create mode 100644 docs/data/data-grid/editing-legacy/BasicRowEditingGrid.tsx create mode 100644 docs/data/data-grid/editing-legacy/BasicRowEditingGrid.tsx.preview create mode 100644 docs/data/data-grid/editing-legacy/CatchEditingEventsGrid.js create mode 100644 docs/data/data-grid/editing-legacy/CatchEditingEventsGrid.tsx create mode 100644 docs/data/data-grid/editing-legacy/CatchEditingEventsGrid.tsx.preview create mode 100644 docs/data/data-grid/editing-legacy/CellEditControlGrid.js create mode 100644 docs/data/data-grid/editing-legacy/CellEditControlGrid.tsx create mode 100644 docs/data/data-grid/editing-legacy/CellEditControlGrid.tsx.preview create mode 100644 docs/data/data-grid/editing-legacy/CellEditServerSidePersistence.js create mode 100644 docs/data/data-grid/editing-legacy/CellEditServerSidePersistence.tsx create mode 100644 docs/data/data-grid/editing-legacy/CellEditServerSidePersistence.tsx.preview create mode 100644 docs/data/data-grid/editing-legacy/ConditionalValidationGrid.js create mode 100644 docs/data/data-grid/editing-legacy/ConditionalValidationGrid.tsx create mode 100644 docs/data/data-grid/editing-legacy/ConditionalValidationGrid.tsx.preview create mode 100644 docs/data/data-grid/editing-legacy/EditApiNoSnap.js create mode 100644 docs/data/data-grid/editing-legacy/FullFeaturedCrudGrid.js create mode 100644 docs/data/data-grid/editing-legacy/FullFeaturedCrudGrid.tsx create mode 100644 docs/data/data-grid/editing-legacy/FullFeaturedCrudGrid.tsx.preview create mode 100644 docs/data/data-grid/editing-legacy/IsCellEditableGrid.js create mode 100644 docs/data/data-grid/editing-legacy/IsCellEditableGrid.tsx create mode 100644 docs/data/data-grid/editing-legacy/IsCellEditableGrid.tsx.preview create mode 100644 docs/data/data-grid/editing-legacy/RenderRatingEditCellGrid.js create mode 100644 docs/data/data-grid/editing-legacy/RenderRatingEditCellGrid.tsx create mode 100644 docs/data/data-grid/editing-legacy/RenderRatingEditCellGrid.tsx.preview create mode 100644 docs/data/data-grid/editing-legacy/RowEditControlGrid.js create mode 100644 docs/data/data-grid/editing-legacy/RowEditControlGrid.tsx create mode 100644 docs/data/data-grid/editing-legacy/RowEditControlGrid.tsx.preview create mode 100644 docs/data/data-grid/editing-legacy/RowEditServerSidePersistence.js create mode 100644 docs/data/data-grid/editing-legacy/RowEditServerSidePersistence.tsx create mode 100644 docs/data/data-grid/editing-legacy/RowEditServerSidePersistence.tsx.preview create mode 100644 docs/data/data-grid/editing-legacy/StartEditButtonGrid.js create mode 100644 docs/data/data-grid/editing-legacy/StartEditButtonGrid.tsx create mode 100644 docs/data/data-grid/editing-legacy/ValidateRowModelControlGrid.js create mode 100644 docs/data/data-grid/editing-legacy/ValidateRowModelControlGrid.tsx create mode 100644 docs/data/data-grid/editing-legacy/ValidateRowModelControlGrid.tsx.preview create mode 100644 docs/data/data-grid/editing-legacy/ValidateServerNameGrid.js create mode 100644 docs/data/data-grid/editing-legacy/ValidateServerNameGrid.tsx create mode 100644 docs/data/data-grid/editing-legacy/ValidateServerNameGrid.tsx.preview create mode 100644 docs/data/data-grid/editing-legacy/ValueGetterSetterGrid.js create mode 100644 docs/data/data-grid/editing-legacy/ValueGetterSetterGrid.tsx create mode 100644 docs/data/data-grid/editing-legacy/ValueGetterSetterGrid.tsx.preview create mode 100644 docs/data/data-grid/editing-legacy/editing.md create mode 100644 docs/data/data-grid/editing/AskConfirmationBeforeSave.js create mode 100644 docs/data/data-grid/editing/AskConfirmationBeforeSave.tsx create mode 100644 docs/data/data-grid/editing/AskConfirmationBeforeSave.tsx.preview create mode 100644 docs/data/data-grid/editing/AutoStopEditComponent.js create mode 100644 docs/data/data-grid/editing/AutoStopEditComponent.tsx create mode 100644 docs/data/data-grid/editing/AutoStopEditComponent.tsx.preview create mode 100644 docs/data/data-grid/editing/BasicEditingGrid.js create mode 100644 docs/data/data-grid/editing/BasicEditingGrid.tsx create mode 100644 docs/data/data-grid/editing/BasicEditingGrid.tsx.preview create mode 100644 docs/data/data-grid/editing/BasicRowEditingGrid.js create mode 100644 docs/data/data-grid/editing/BasicRowEditingGrid.tsx create mode 100644 docs/data/data-grid/editing/BasicRowEditingGrid.tsx.preview create mode 100644 docs/data/data-grid/editing/ConditionalValidationGrid.js create mode 100644 docs/data/data-grid/editing/ConditionalValidationGrid.tsx create mode 100644 docs/data/data-grid/editing/ConditionalValidationGrid.tsx.preview create mode 100644 docs/data/data-grid/editing/CustomEditComponent.js create mode 100644 docs/data/data-grid/editing/CustomEditComponent.tsx create mode 100644 docs/data/data-grid/editing/CustomEditComponent.tsx.preview create mode 100644 docs/data/data-grid/editing/DisableStopEditModeOnFocusOut.js create mode 100644 docs/data/data-grid/editing/DisableStopEditModeOnFocusOut.tsx create mode 100644 docs/data/data-grid/editing/DisableStopEditModeOnFocusOut.tsx.preview create mode 100644 docs/data/data-grid/editing/EditApiNoSnap.js create mode 100644 docs/data/data-grid/editing/FullFeaturedCrudGrid.js create mode 100644 docs/data/data-grid/editing/FullFeaturedCrudGrid.tsx create mode 100644 docs/data/data-grid/editing/FullFeaturedCrudGrid.tsx.preview create mode 100644 docs/data/data-grid/editing/IsCellEditableGrid.js create mode 100644 docs/data/data-grid/editing/IsCellEditableGrid.tsx create mode 100644 docs/data/data-grid/editing/IsCellEditableGrid.tsx.preview create mode 100644 docs/data/data-grid/editing/LinkedFieldsCellEditing.js create mode 100644 docs/data/data-grid/editing/LinkedFieldsCellEditing.tsx create mode 100644 docs/data/data-grid/editing/LinkedFieldsCellEditing.tsx.preview create mode 100644 docs/data/data-grid/editing/LinkedFieldsRowEditing.js create mode 100644 docs/data/data-grid/editing/LinkedFieldsRowEditing.tsx create mode 100644 docs/data/data-grid/editing/LinkedFieldsRowEditing.tsx.preview create mode 100644 docs/data/data-grid/editing/ServerSidePersistence.js create mode 100644 docs/data/data-grid/editing/ServerSidePersistence.tsx create mode 100644 docs/data/data-grid/editing/StartEditButtonGrid.js create mode 100644 docs/data/data-grid/editing/StartEditButtonGrid.tsx create mode 100644 docs/data/data-grid/editing/ValidateServerNameGrid.js create mode 100644 docs/data/data-grid/editing/ValidateServerNameGrid.tsx create mode 100644 docs/data/data-grid/editing/ValidateServerNameGrid.tsx.preview create mode 100644 docs/data/data-grid/editing/ValueParserSetterGrid.js create mode 100644 docs/data/data-grid/editing/ValueParserSetterGrid.tsx create mode 100644 docs/data/data-grid/editing/ValueParserSetterGrid.tsx.preview create mode 100644 docs/data/data-grid/editing/editing.md create mode 100644 docs/data/data-grid/events/CatalogOfEventsNoSnap.js create mode 100644 docs/data/data-grid/events/DoubleClickWithCtrlToEdit.js create mode 100644 docs/data/data-grid/events/DoubleClickWithCtrlToEdit.tsx create mode 100644 docs/data/data-grid/events/DoubleClickWithCtrlToEdit.tsx.preview create mode 100644 docs/data/data-grid/events/SubscribeToEvents.js create mode 100644 docs/data/data-grid/events/SubscribeToEvents.tsx create mode 100644 docs/data/data-grid/events/SubscribeToEvents.tsx.preview create mode 100644 docs/data/data-grid/events/events.json create mode 100644 docs/data/data-grid/events/events.md create mode 100644 docs/data/data-grid/export/CsvExportApiNoSnap.js create mode 100644 docs/data/data-grid/export/CsvGetRowsToExport.js create mode 100644 docs/data/data-grid/export/CsvGetRowsToExport.tsx create mode 100644 docs/data/data-grid/export/CsvGetRowsToExport.tsx.preview create mode 100644 docs/data/data-grid/export/CsvGetRowsToExportRowGrouping.js create mode 100644 docs/data/data-grid/export/CsvGetRowsToExportRowGrouping.tsx create mode 100644 docs/data/data-grid/export/CsvGetRowsToExportRowGrouping.tsx.preview create mode 100644 docs/data/data-grid/export/CustomExport.js create mode 100644 docs/data/data-grid/export/CustomExport.tsx create mode 100644 docs/data/data-grid/export/CustomExport.tsx.preview create mode 100644 docs/data/data-grid/export/ExcelCustomExport.js create mode 100644 docs/data/data-grid/export/ExcelCustomExport.tsx create mode 100644 docs/data/data-grid/export/ExcelCustomExport.tsx.preview create mode 100644 docs/data/data-grid/export/ExcelExport.js create mode 100644 docs/data/data-grid/export/ExcelExport.tsx create mode 100644 docs/data/data-grid/export/ExcelExport.tsx.preview create mode 100644 docs/data/data-grid/export/ExcelExportApiNoSnap.js create mode 100644 docs/data/data-grid/export/ExportCustomToolbar.js create mode 100644 docs/data/data-grid/export/ExportCustomToolbar.tsx create mode 100644 docs/data/data-grid/export/ExportCustomToolbar.tsx.preview create mode 100644 docs/data/data-grid/export/ExportDefaultToolbar.js create mode 100644 docs/data/data-grid/export/ExportDefaultToolbar.tsx create mode 100644 docs/data/data-grid/export/ExportDefaultToolbar.tsx.preview create mode 100644 docs/data/data-grid/export/PrintExportApiNoSnap.js create mode 100644 docs/data/data-grid/export/RemovePrintExport.js create mode 100644 docs/data/data-grid/export/RemovePrintExport.tsx create mode 100644 docs/data/data-grid/export/RemovePrintExport.tsx.preview create mode 100644 docs/data/data-grid/export/export.md create mode 100644 docs/data/data-grid/filtering/BasicExampleDataGrid.js create mode 100644 docs/data/data-grid/filtering/BasicExampleDataGrid.tsx create mode 100644 docs/data/data-grid/filtering/BasicExampleDataGrid.tsx.preview create mode 100644 docs/data/data-grid/filtering/BasicExampleDataGridPro.js create mode 100644 docs/data/data-grid/filtering/BasicExampleDataGridPro.tsx create mode 100644 docs/data/data-grid/filtering/BasicExampleDataGridPro.tsx.preview create mode 100644 docs/data/data-grid/filtering/ControlledFilters.js create mode 100644 docs/data/data-grid/filtering/ControlledFilters.tsx create mode 100644 docs/data/data-grid/filtering/ControlledFilters.tsx.preview create mode 100644 docs/data/data-grid/filtering/CustomFilterPanelContent.js create mode 100644 docs/data/data-grid/filtering/CustomFilterPanelContent.tsx create mode 100644 docs/data/data-grid/filtering/CustomFilterPanelPosition.js create mode 100644 docs/data/data-grid/filtering/CustomFilterPanelPosition.tsx create mode 100644 docs/data/data-grid/filtering/CustomFilterPanelPosition.tsx.preview create mode 100644 docs/data/data-grid/filtering/CustomInputComponent.js create mode 100644 docs/data/data-grid/filtering/CustomInputComponent.tsx create mode 100644 docs/data/data-grid/filtering/CustomInputComponent.tsx.preview create mode 100644 docs/data/data-grid/filtering/CustomMultiValueOperator.js create mode 100644 docs/data/data-grid/filtering/CustomMultiValueOperator.tsx create mode 100644 docs/data/data-grid/filtering/CustomMultiValueOperator.tsx.preview create mode 100644 docs/data/data-grid/filtering/CustomRatingOperator.js create mode 100644 docs/data/data-grid/filtering/CustomRatingOperator.tsx create mode 100644 docs/data/data-grid/filtering/CustomSelectionOperator.js create mode 100644 docs/data/data-grid/filtering/CustomSelectionOperator.tsx create mode 100644 docs/data/data-grid/filtering/CustomSelectionOperator.tsx.preview create mode 100644 docs/data/data-grid/filtering/DisableFilteringGridAllColumns.js create mode 100644 docs/data/data-grid/filtering/DisableFilteringGridAllColumns.tsx create mode 100644 docs/data/data-grid/filtering/DisableFilteringGridAllColumns.tsx.preview create mode 100644 docs/data/data-grid/filtering/DisableFilteringGridSomeColumns.js create mode 100644 docs/data/data-grid/filtering/DisableFilteringGridSomeColumns.tsx create mode 100644 docs/data/data-grid/filtering/DisableFilteringGridSomeColumns.tsx.preview create mode 100644 docs/data/data-grid/filtering/FilterApiNoSnap.js create mode 100644 docs/data/data-grid/filtering/FilterSelectorsNoSnap.js create mode 100644 docs/data/data-grid/filtering/InitialFilters.js create mode 100644 docs/data/data-grid/filtering/InitialFilters.tsx create mode 100644 docs/data/data-grid/filtering/QuickFilteringCustomLogic.js create mode 100644 docs/data/data-grid/filtering/QuickFilteringCustomLogic.tsx create mode 100644 docs/data/data-grid/filtering/QuickFilteringCustomLogic.tsx.preview create mode 100644 docs/data/data-grid/filtering/QuickFilteringCustomizedGrid.js create mode 100644 docs/data/data-grid/filtering/QuickFilteringCustomizedGrid.tsx create mode 100644 docs/data/data-grid/filtering/QuickFilteringCustomizedGrid.tsx.preview create mode 100644 docs/data/data-grid/filtering/QuickFilteringGrid.js create mode 100644 docs/data/data-grid/filtering/QuickFilteringGrid.tsx create mode 100644 docs/data/data-grid/filtering/QuickFilteringGrid.tsx.preview create mode 100644 docs/data/data-grid/filtering/RemoveBuiltInOperators.js create mode 100644 docs/data/data-grid/filtering/RemoveBuiltInOperators.tsx create mode 100644 docs/data/data-grid/filtering/RemoveBuiltInOperators.tsx.preview create mode 100644 docs/data/data-grid/filtering/ServerFilterGrid.js create mode 100644 docs/data/data-grid/filtering/ServerFilterGrid.tsx create mode 100644 docs/data/data-grid/filtering/ServerFilterGrid.tsx.preview create mode 100644 docs/data/data-grid/filtering/filtering.md create mode 100644 docs/data/data-grid/getting-started/Codesandbox.js create mode 100644 docs/data/data-grid/getting-started/CoreV5WithCoreV4.js create mode 100644 docs/data/data-grid/getting-started/getting-started.md create mode 100644 docs/data/data-grid/getting-started/migration-v4.md create mode 100644 docs/data/data-grid/layout/AutoHeightGrid.js create mode 100644 docs/data/data-grid/layout/AutoHeightGrid.tsx create mode 100644 docs/data/data-grid/layout/AutoHeightGrid.tsx.preview create mode 100644 docs/data/data-grid/layout/FixedSizeGrid.js create mode 100644 docs/data/data-grid/layout/FixedSizeGrid.tsx create mode 100644 docs/data/data-grid/layout/FixedSizeGrid.tsx.preview create mode 100644 docs/data/data-grid/layout/FlexLayoutGrid.js create mode 100644 docs/data/data-grid/layout/FlexLayoutGrid.tsx create mode 100644 docs/data/data-grid/layout/FlexLayoutGrid.tsx.preview create mode 100644 docs/data/data-grid/layout/layout.md create mode 100644 docs/data/data-grid/localization/CustomLocaleTextGrid.js create mode 100644 docs/data/data-grid/localization/CustomLocaleTextGrid.tsx create mode 100644 docs/data/data-grid/localization/CustomLocaleTextGrid.tsx.preview create mode 100644 docs/data/data-grid/localization/localization.md create mode 100644 docs/data/data-grid/master-detail/BasicDetailPanels.js create mode 100644 docs/data/data-grid/master-detail/BasicDetailPanels.tsx create mode 100644 docs/data/data-grid/master-detail/BasicDetailPanels.tsx.preview create mode 100644 docs/data/data-grid/master-detail/ControlMasterDetail.js create mode 100644 docs/data/data-grid/master-detail/ControlMasterDetail.tsx create mode 100644 docs/data/data-grid/master-detail/CustomizeDetailPanelToggle.js create mode 100644 docs/data/data-grid/master-detail/CustomizeDetailPanelToggle.tsx create mode 100644 docs/data/data-grid/master-detail/CustomizeDetailPanelToggle.tsx.preview create mode 100644 docs/data/data-grid/master-detail/DetailPanelApiNoSnap.js create mode 100644 docs/data/data-grid/master-detail/FormDetailPanel.js create mode 100644 docs/data/data-grid/master-detail/FormDetailPanel.tsx create mode 100644 docs/data/data-grid/master-detail/FormDetailPanel.tsx.preview create mode 100644 docs/data/data-grid/master-detail/FullWidthDetailPanel.js create mode 100644 docs/data/data-grid/master-detail/FullWidthDetailPanel.tsx create mode 100644 docs/data/data-grid/master-detail/FullWidthDetailPanel.tsx.preview create mode 100644 docs/data/data-grid/master-detail/master-detail.md create mode 100644 docs/data/data-grid/overview/DataGridDemo.js create mode 100644 docs/data/data-grid/overview/DataGridDemo.tsx create mode 100644 docs/data/data-grid/overview/DataGridDemo.tsx.preview create mode 100644 docs/data/data-grid/overview/DataGridPremiumDemo.js create mode 100644 docs/data/data-grid/overview/DataGridPremiumDemo.tsx create mode 100644 docs/data/data-grid/overview/DataGridPremiumDemo.tsx.preview create mode 100644 docs/data/data-grid/overview/DataGridProDemo.js create mode 100644 docs/data/data-grid/overview/DataGridProDemo.tsx create mode 100644 docs/data/data-grid/overview/DataGridProDemo.tsx.preview create mode 100644 docs/data/data-grid/overview/overview.md create mode 100644 docs/data/data-grid/pagination/CursorPaginationGrid.js create mode 100644 docs/data/data-grid/pagination/CursorPaginationGrid.tsx create mode 100644 docs/data/data-grid/pagination/CursorPaginationGrid.tsx.preview create mode 100644 docs/data/data-grid/pagination/PageControlled.js create mode 100644 docs/data/data-grid/pagination/PageControlled.tsx create mode 100644 docs/data/data-grid/pagination/PageControlled.tsx.preview create mode 100644 docs/data/data-grid/pagination/PageInitialState.js create mode 100644 docs/data/data-grid/pagination/PageInitialState.tsx create mode 100644 docs/data/data-grid/pagination/PageInitialState.tsx.preview create mode 100644 docs/data/data-grid/pagination/PageSizeAuto.js create mode 100644 docs/data/data-grid/pagination/PageSizeAuto.tsx create mode 100644 docs/data/data-grid/pagination/PageSizeControlled.js create mode 100644 docs/data/data-grid/pagination/PageSizeControlled.tsx create mode 100644 docs/data/data-grid/pagination/PageSizeControlled.tsx.preview create mode 100644 docs/data/data-grid/pagination/PageSizeCustomOptions.js create mode 100644 docs/data/data-grid/pagination/PageSizeCustomOptions.tsx create mode 100644 docs/data/data-grid/pagination/PageSizeCustomOptions.tsx.preview create mode 100644 docs/data/data-grid/pagination/PageSizeInitialState.js create mode 100644 docs/data/data-grid/pagination/PageSizeInitialState.tsx create mode 100644 docs/data/data-grid/pagination/PageSizeInitialState.tsx.preview create mode 100644 docs/data/data-grid/pagination/PaginationApiNoSnap.js create mode 100644 docs/data/data-grid/pagination/PaginationSelectorsNoSnap.js create mode 100644 docs/data/data-grid/pagination/ServerPaginationGrid.js create mode 100644 docs/data/data-grid/pagination/ServerPaginationGrid.tsx create mode 100644 docs/data/data-grid/pagination/ServerPaginationGrid.tsx.preview create mode 100644 docs/data/data-grid/pagination/pagination.md create mode 100644 docs/data/data-grid/pivoting/pivoting.md create mode 100644 docs/data/data-grid/row-grouping/RowGroupingApiNoSnap.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingBasicExample.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingBasicExample.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingBasicExample.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingColDefCanBeGrouped.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingColDefCanBeGrouped.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingColDefCanBeGrouped.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingControlled.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingControlled.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingControlled.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingCustomGroupingColDefCallback.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingCustomGroupingColDefCallback.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingCustomGroupingColDefObject.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingCustomGroupingColDefObject.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingCustomGroupingColDefObject.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingDefaultExpansionDepth.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingDefaultExpansionDepth.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingDefaultExpansionDepth.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingDisabled.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingDisabled.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingDisabled.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingFullExample.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingFullExample.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingFullExample.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingGetRowGroupChildren.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingGetRowGroupChildren.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingGroupingValueGetter.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingGroupingValueGetter.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingGroupingValueGetter.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingHideDescendantCount.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingHideDescendantCount.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingHideDescendantCount.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingInitialState.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingInitialState.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingInitialState.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingIsGroupExpandedByDefault.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingIsGroupExpandedByDefault.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingIsGroupExpandedByDefault.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingLeafWithValue.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingLeafWithValue.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingLeafWithValue.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingMultipleGroupingCol.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingMultipleGroupingCol.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingMultipleGroupingCol.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingRowsWithMissingGroups.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingRowsWithMissingGroups.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingRowsWithMissingGroups.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingSetChildrenExpansion.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingSetChildrenExpansion.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingSetChildrenExpansion.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingSingleGroupingCol.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingSingleGroupingCol.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingSingleGroupingCol.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingSortingMultipleGroupingColDef.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingSortingMultipleGroupingColDef.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingSortingMultipleGroupingColDef.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/RowGroupingSortingSingleGroupingColDef.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingSortingSingleGroupingColDef.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingUseKeepGroupedColumnsHidden.js create mode 100644 docs/data/data-grid/row-grouping/RowGroupingUseKeepGroupedColumnsHidden.tsx create mode 100644 docs/data/data-grid/row-grouping/RowGroupingUseKeepGroupedColumnsHidden.tsx.preview create mode 100644 docs/data/data-grid/row-grouping/row-grouping.md create mode 100644 docs/data/data-grid/rows/DenseHeightGrid.js create mode 100644 docs/data/data-grid/rows/DenseHeightGrid.tsx create mode 100644 docs/data/data-grid/rows/DenseHeightGrid.tsx.preview create mode 100644 docs/data/data-grid/rows/DynamicRowHeightGrid.js create mode 100644 docs/data/data-grid/rows/DynamicRowHeightGrid.tsx create mode 100644 docs/data/data-grid/rows/DynamicRowHeightGrid.tsx.preview create mode 100644 docs/data/data-grid/rows/ExpandableCells.js create mode 100644 docs/data/data-grid/rows/ExpandableCells.tsx create mode 100644 docs/data/data-grid/rows/InfiniteLoadingGrid.js create mode 100644 docs/data/data-grid/rows/InfiniteLoadingGrid.tsx create mode 100644 docs/data/data-grid/rows/InfiniteLoadingGrid.tsx.preview create mode 100644 docs/data/data-grid/rows/RowMarginGrid.js create mode 100644 docs/data/data-grid/rows/RowMarginGrid.tsx create mode 100644 docs/data/data-grid/rows/RowMarginGrid.tsx.preview create mode 100644 docs/data/data-grid/rows/RowOrderingGrid.js create mode 100644 docs/data/data-grid/rows/RowOrderingGrid.tsx create mode 100644 docs/data/data-grid/rows/RowOrderingGrid.tsx.preview create mode 100644 docs/data/data-grid/rows/RowsGrid.js create mode 100644 docs/data/data-grid/rows/RowsGrid.tsx create mode 100644 docs/data/data-grid/rows/RowsGrid.tsx.preview create mode 100644 docs/data/data-grid/rows/RowsGridWithGetRowId.js create mode 100644 docs/data/data-grid/rows/RowsGridWithGetRowId.tsx create mode 100644 docs/data/data-grid/rows/RowsGridWithGetRowId.tsx.preview create mode 100644 docs/data/data-grid/rows/ThrottledRowsGrid.js create mode 100644 docs/data/data-grid/rows/ThrottledRowsGrid.tsx create mode 100644 docs/data/data-grid/rows/ThrottledRowsGrid.tsx.preview create mode 100644 docs/data/data-grid/rows/UpdateRowsApiRef.js create mode 100644 docs/data/data-grid/rows/UpdateRowsApiRef.tsx create mode 100644 docs/data/data-grid/rows/UpdateRowsProp.js create mode 100644 docs/data/data-grid/rows/UpdateRowsProp.tsx create mode 100644 docs/data/data-grid/rows/VariableRowHeightGrid.js create mode 100644 docs/data/data-grid/rows/VariableRowHeightGrid.tsx create mode 100644 docs/data/data-grid/rows/VariableRowHeightGrid.tsx.preview create mode 100644 docs/data/data-grid/rows/rows.md create mode 100644 docs/data/data-grid/scrolling/ScrollApiNoSnap.js create mode 100644 docs/data/data-grid/scrolling/ScrollPlayground.js create mode 100644 docs/data/data-grid/scrolling/ScrollPlayground.tsx create mode 100644 docs/data/data-grid/scrolling/scrolling.md create mode 100644 docs/data/data-grid/selection/CheckboxSelectionGrid.js create mode 100644 docs/data/data-grid/selection/CheckboxSelectionGrid.tsx create mode 100644 docs/data/data-grid/selection/CheckboxSelectionGrid.tsx.preview create mode 100644 docs/data/data-grid/selection/ControlledSelectionGrid.js create mode 100644 docs/data/data-grid/selection/ControlledSelectionGrid.tsx create mode 100644 docs/data/data-grid/selection/ControlledSelectionGrid.tsx.preview create mode 100644 docs/data/data-grid/selection/ControlledSelectionServerPaginationGrid.js create mode 100644 docs/data/data-grid/selection/ControlledSelectionServerPaginationGrid.tsx create mode 100644 docs/data/data-grid/selection/DisableClickSelectionGrid.js create mode 100644 docs/data/data-grid/selection/DisableClickSelectionGrid.tsx create mode 100644 docs/data/data-grid/selection/DisableClickSelectionGrid.tsx.preview create mode 100644 docs/data/data-grid/selection/DisableRowSelection.js create mode 100644 docs/data/data-grid/selection/DisableRowSelection.tsx create mode 100644 docs/data/data-grid/selection/DisableRowSelection.tsx.preview create mode 100644 docs/data/data-grid/selection/MultipleRowSelectionGrid.js create mode 100644 docs/data/data-grid/selection/MultipleRowSelectionGrid.tsx create mode 100644 docs/data/data-grid/selection/MultipleRowSelectionGrid.tsx.preview create mode 100644 docs/data/data-grid/selection/SelectionApiNoSnap.js create mode 100644 docs/data/data-grid/selection/SingleRowSelectionGrid.js create mode 100644 docs/data/data-grid/selection/SingleRowSelectionGrid.tsx create mode 100644 docs/data/data-grid/selection/SingleRowSelectionGrid.tsx.preview create mode 100644 docs/data/data-grid/selection/selection.md create mode 100644 docs/data/data-grid/sorting/BasicExampleDataGrid.js create mode 100644 docs/data/data-grid/sorting/BasicExampleDataGrid.tsx create mode 100644 docs/data/data-grid/sorting/BasicExampleDataGrid.tsx.preview create mode 100644 docs/data/data-grid/sorting/BasicExampleDataGridPro.js create mode 100644 docs/data/data-grid/sorting/BasicExampleDataGridPro.tsx create mode 100644 docs/data/data-grid/sorting/BasicExampleDataGridPro.tsx.preview create mode 100644 docs/data/data-grid/sorting/ControlledSort.js create mode 100644 docs/data/data-grid/sorting/ControlledSort.tsx create mode 100644 docs/data/data-grid/sorting/ControlledSort.tsx.preview create mode 100644 docs/data/data-grid/sorting/DisableSortingGrid.js create mode 100644 docs/data/data-grid/sorting/DisableSortingGrid.tsx create mode 100644 docs/data/data-grid/sorting/DisableSortingGrid.tsx.preview create mode 100644 docs/data/data-grid/sorting/ExtendedSortComparator.js create mode 100644 docs/data/data-grid/sorting/ExtendedSortComparator.tsx create mode 100644 docs/data/data-grid/sorting/ExtendedSortComparator.tsx.preview create mode 100644 docs/data/data-grid/sorting/FullyCustomSortComparator.js create mode 100644 docs/data/data-grid/sorting/FullyCustomSortComparator.tsx create mode 100644 docs/data/data-grid/sorting/FullyCustomSortComparator.tsx.preview create mode 100644 docs/data/data-grid/sorting/InitialSort.js create mode 100644 docs/data/data-grid/sorting/InitialSort.tsx create mode 100644 docs/data/data-grid/sorting/InitialSort.tsx.preview create mode 100644 docs/data/data-grid/sorting/OrderSortingGrid.js create mode 100644 docs/data/data-grid/sorting/OrderSortingGrid.tsx create mode 100644 docs/data/data-grid/sorting/OrderSortingGrid.tsx.preview create mode 100644 docs/data/data-grid/sorting/OrderSortingPerColumnGrid.js create mode 100644 docs/data/data-grid/sorting/OrderSortingPerColumnGrid.tsx create mode 100644 docs/data/data-grid/sorting/OrderSortingPerColumnGrid.tsx.preview create mode 100644 docs/data/data-grid/sorting/ServerSortingGrid.js create mode 100644 docs/data/data-grid/sorting/ServerSortingGrid.tsx create mode 100644 docs/data/data-grid/sorting/ServerSortingGrid.tsx.preview create mode 100644 docs/data/data-grid/sorting/SortingApiNoSnap.js create mode 100644 docs/data/data-grid/sorting/SortingSelectorsNoSnap.js create mode 100644 docs/data/data-grid/sorting/sorting.md create mode 100644 docs/data/data-grid/state/DirectSelector.js create mode 100644 docs/data/data-grid/state/DirectSelector.tsx create mode 100644 docs/data/data-grid/state/DirectSelector.tsx.preview create mode 100644 docs/data/data-grid/state/InitialState.js create mode 100644 docs/data/data-grid/state/InitialState.tsx create mode 100644 docs/data/data-grid/state/InitialState.tsx.preview create mode 100644 docs/data/data-grid/state/RestoreStateApiRef.js create mode 100644 docs/data/data-grid/state/RestoreStateApiRef.tsx create mode 100644 docs/data/data-grid/state/RestoreStateApiRef.tsx.preview create mode 100644 docs/data/data-grid/state/RestoreStateInitialState.js create mode 100644 docs/data/data-grid/state/RestoreStateInitialState.tsx create mode 100644 docs/data/data-grid/state/RestoreStateInitialState.tsx.preview create mode 100644 docs/data/data-grid/state/SelectorsNoSnap.js create mode 100644 docs/data/data-grid/state/UseGridSelector.js create mode 100644 docs/data/data-grid/state/UseGridSelector.tsx create mode 100644 docs/data/data-grid/state/UseGridSelector.tsx.preview create mode 100644 docs/data/data-grid/state/state.md create mode 100644 docs/data/data-grid/style/AntDesignGrid.js create mode 100644 docs/data/data-grid/style/AntDesignGrid.tsx create mode 100644 docs/data/data-grid/style/AntDesignGrid.tsx.preview create mode 100644 docs/data/data-grid/style/StripedGrid.js create mode 100644 docs/data/data-grid/style/StripedGrid.tsx create mode 100644 docs/data/data-grid/style/StripedGrid.tsx.preview create mode 100644 docs/data/data-grid/style/StylingAllCells.js create mode 100644 docs/data/data-grid/style/StylingAllCells.tsx create mode 100644 docs/data/data-grid/style/StylingAllCells.tsx.preview create mode 100644 docs/data/data-grid/style/StylingCellsGrid.js create mode 100644 docs/data/data-grid/style/StylingCellsGrid.tsx create mode 100644 docs/data/data-grid/style/StylingCellsGrid.tsx.preview create mode 100644 docs/data/data-grid/style/StylingHeaderGrid.js create mode 100644 docs/data/data-grid/style/StylingHeaderGrid.tsx create mode 100644 docs/data/data-grid/style/StylingHeaderGrid.tsx.preview create mode 100644 docs/data/data-grid/style/StylingRowsGrid.js create mode 100644 docs/data/data-grid/style/StylingRowsGrid.tsx create mode 100644 docs/data/data-grid/style/StylingRowsGrid.tsx.preview create mode 100644 docs/data/data-grid/style/SxProp.js create mode 100644 docs/data/data-grid/style/SxProp.tsx create mode 100644 docs/data/data-grid/style/SxProp.tsx.preview create mode 100644 docs/data/data-grid/style/style.md create mode 100644 docs/data/data-grid/tree-data/TreeDataCustomGroupingColumn.js create mode 100644 docs/data/data-grid/tree-data/TreeDataCustomGroupingColumn.tsx create mode 100644 docs/data/data-grid/tree-data/TreeDataCustomGroupingColumn.tsx.preview create mode 100644 docs/data/data-grid/tree-data/TreeDataDisableChildrenFiltering.js create mode 100644 docs/data/data-grid/tree-data/TreeDataDisableChildrenFiltering.tsx create mode 100644 docs/data/data-grid/tree-data/TreeDataDisableChildrenSorting.js create mode 100644 docs/data/data-grid/tree-data/TreeDataDisableChildrenSorting.tsx create mode 100644 docs/data/data-grid/tree-data/TreeDataFullExample.js create mode 100644 docs/data/data-grid/tree-data/TreeDataFullExample.tsx create mode 100644 docs/data/data-grid/tree-data/TreeDataFullExample.tsx.preview create mode 100644 docs/data/data-grid/tree-data/TreeDataLazyLoading.js create mode 100644 docs/data/data-grid/tree-data/TreeDataLazyLoading.tsx create mode 100644 docs/data/data-grid/tree-data/TreeDataLazyLoading.tsx.preview create mode 100644 docs/data/data-grid/tree-data/TreeDataSimple.js create mode 100644 docs/data/data-grid/tree-data/TreeDataSimple.tsx create mode 100644 docs/data/data-grid/tree-data/TreeDataSimple.tsx.preview create mode 100644 docs/data/data-grid/tree-data/TreeDataWithGap.js create mode 100644 docs/data/data-grid/tree-data/TreeDataWithGap.tsx create mode 100644 docs/data/data-grid/tree-data/TreeDataWithGap.tsx.preview create mode 100644 docs/data/data-grid/tree-data/tree-data.md create mode 100644 docs/data/data-grid/virtualization/ColumnVirtualizationGrid.js create mode 100644 docs/data/data-grid/virtualization/ColumnVirtualizationGrid.tsx create mode 100644 docs/data/data-grid/virtualization/ColumnVirtualizationGrid.tsx.preview create mode 100644 docs/data/data-grid/virtualization/virtualization.md create mode 100644 docs/data/date-pickers/date-picker/BasicDatePicker.js create mode 100644 docs/data/date-pickers/date-picker/BasicDatePicker.tsx create mode 100644 docs/data/date-pickers/date-picker/BasicDatePicker.tsx.preview create mode 100644 docs/data/date-pickers/date-picker/CustomDay.js create mode 100644 docs/data/date-pickers/date-picker/CustomDay.tsx create mode 100644 docs/data/date-pickers/date-picker/CustomDay.tsx.preview create mode 100644 docs/data/date-pickers/date-picker/CustomInput.js create mode 100644 docs/data/date-pickers/date-picker/CustomInput.tsx create mode 100644 docs/data/date-pickers/date-picker/CustomInput.tsx.preview create mode 100644 docs/data/date-pickers/date-picker/FormPropsDatePickers.js create mode 100644 docs/data/date-pickers/date-picker/FormPropsDatePickers.tsx create mode 100644 docs/data/date-pickers/date-picker/HelperText.js create mode 100644 docs/data/date-pickers/date-picker/HelperText.tsx create mode 100644 docs/data/date-pickers/date-picker/HelperText.tsx.preview create mode 100644 docs/data/date-pickers/date-picker/JalaliDatePicker.js create mode 100644 docs/data/date-pickers/date-picker/JalaliDatePicker.tsx create mode 100644 docs/data/date-pickers/date-picker/JalaliDatePicker.tsx.preview create mode 100644 docs/data/date-pickers/date-picker/LocalizedDatePicker.js create mode 100644 docs/data/date-pickers/date-picker/LocalizedDatePicker.tsx create mode 100644 docs/data/date-pickers/date-picker/ResponsiveDatePickers.js create mode 100644 docs/data/date-pickers/date-picker/ResponsiveDatePickers.tsx create mode 100644 docs/data/date-pickers/date-picker/ServerRequestDatePicker.js create mode 100644 docs/data/date-pickers/date-picker/ServerRequestDatePicker.tsx create mode 100644 docs/data/date-pickers/date-picker/StaticDatePickerDemo.js create mode 100644 docs/data/date-pickers/date-picker/StaticDatePickerDemo.tsx create mode 100644 docs/data/date-pickers/date-picker/StaticDatePickerDemo.tsx.preview create mode 100644 docs/data/date-pickers/date-picker/StaticDatePickerLandscape.js create mode 100644 docs/data/date-pickers/date-picker/StaticDatePickerLandscape.tsx create mode 100644 docs/data/date-pickers/date-picker/StaticDatePickerLandscape.tsx.preview create mode 100644 docs/data/date-pickers/date-picker/SubComponentsPickers.js create mode 100644 docs/data/date-pickers/date-picker/SubComponentsPickers.tsx create mode 100644 docs/data/date-pickers/date-picker/ViewsDatePicker.js create mode 100644 docs/data/date-pickers/date-picker/ViewsDatePicker.tsx create mode 100644 docs/data/date-pickers/date-picker/date-picker-pt.md create mode 100644 docs/data/date-pickers/date-picker/date-picker-zh.md create mode 100644 docs/data/date-pickers/date-picker/date-picker.md create mode 100644 docs/data/date-pickers/date-range-picker/BasicDateRangePicker.js create mode 100644 docs/data/date-pickers/date-range-picker/BasicDateRangePicker.tsx create mode 100644 docs/data/date-pickers/date-range-picker/CalendarsDateRangePicker.js create mode 100644 docs/data/date-pickers/date-range-picker/CalendarsDateRangePicker.tsx create mode 100644 docs/data/date-pickers/date-range-picker/CustomDateRangeInputs.js create mode 100644 docs/data/date-pickers/date-range-picker/CustomDateRangeInputs.tsx create mode 100644 docs/data/date-pickers/date-range-picker/CustomDateRangePickerDay.js create mode 100644 docs/data/date-pickers/date-range-picker/CustomDateRangePickerDay.tsx create mode 100644 docs/data/date-pickers/date-range-picker/CustomDateRangePickerDay.tsx.preview create mode 100644 docs/data/date-pickers/date-range-picker/FormPropsDateRangePickers.js create mode 100644 docs/data/date-pickers/date-range-picker/FormPropsDateRangePickers.tsx create mode 100644 docs/data/date-pickers/date-range-picker/MinMaxDateRangePicker.js create mode 100644 docs/data/date-pickers/date-range-picker/MinMaxDateRangePicker.tsx create mode 100644 docs/data/date-pickers/date-range-picker/ResponsiveDateRangePicker.js create mode 100644 docs/data/date-pickers/date-range-picker/ResponsiveDateRangePicker.tsx create mode 100644 docs/data/date-pickers/date-range-picker/StaticDateRangePickerDemo.js create mode 100644 docs/data/date-pickers/date-range-picker/StaticDateRangePickerDemo.tsx create mode 100644 docs/data/date-pickers/date-range-picker/StaticDateRangePickerDemo.tsx.preview create mode 100644 docs/data/date-pickers/date-range-picker/date-range-picker-pt.md create mode 100644 docs/data/date-pickers/date-range-picker/date-range-picker-zh.md create mode 100644 docs/data/date-pickers/date-range-picker/date-range-picker.md create mode 100644 docs/data/date-pickers/date-time-picker/BasicDateTimePicker.js create mode 100644 docs/data/date-pickers/date-time-picker/BasicDateTimePicker.tsx create mode 100644 docs/data/date-pickers/date-time-picker/BasicDateTimePicker.tsx.preview create mode 100644 docs/data/date-pickers/date-time-picker/CustomDateTimePicker.js create mode 100644 docs/data/date-pickers/date-time-picker/CustomDateTimePicker.tsx create mode 100644 docs/data/date-pickers/date-time-picker/DateTimeValidation.js create mode 100644 docs/data/date-pickers/date-time-picker/DateTimeValidation.tsx create mode 100644 docs/data/date-pickers/date-time-picker/FormPropsDateTimePickers.js create mode 100644 docs/data/date-pickers/date-time-picker/FormPropsDateTimePickers.tsx create mode 100644 docs/data/date-pickers/date-time-picker/ResponsiveDateTimePickers.js create mode 100644 docs/data/date-pickers/date-time-picker/ResponsiveDateTimePickers.tsx create mode 100644 docs/data/date-pickers/date-time-picker/StaticDateTimePickerDemo.js create mode 100644 docs/data/date-pickers/date-time-picker/StaticDateTimePickerDemo.tsx create mode 100644 docs/data/date-pickers/date-time-picker/StaticDateTimePickerDemo.tsx.preview create mode 100644 docs/data/date-pickers/date-time-picker/date-time-picker-pt.md create mode 100644 docs/data/date-pickers/date-time-picker/date-time-picker-zh.md create mode 100644 docs/data/date-pickers/date-time-picker/date-time-picker.md create mode 100644 docs/data/date-pickers/date-time-range-picker/date-time-range-picker.md create mode 100644 docs/data/date-pickers/getting-started/MaterialUIPickers.js create mode 100644 docs/data/date-pickers/getting-started/MaterialUIPickers.tsx create mode 100644 docs/data/date-pickers/getting-started/NativePickers.js create mode 100644 docs/data/date-pickers/getting-started/NativePickers.tsx create mode 100644 docs/data/date-pickers/getting-started/getting-started-pt.md create mode 100644 docs/data/date-pickers/getting-started/getting-started-zh.md create mode 100644 docs/data/date-pickers/getting-started/getting-started.md create mode 100644 docs/data/date-pickers/localization/LocalizedDatePicker.js create mode 100644 docs/data/date-pickers/localization/LocalizedDatePicker.tsx create mode 100644 docs/data/date-pickers/localization/localization.md create mode 100644 docs/data/date-pickers/migration-lab/migration-lab.md create mode 100644 docs/data/date-pickers/time-picker/BasicTimePicker.js create mode 100644 docs/data/date-pickers/time-picker/BasicTimePicker.tsx create mode 100644 docs/data/date-pickers/time-picker/BasicTimePicker.tsx.preview create mode 100644 docs/data/date-pickers/time-picker/FormPropsTimePickers.js create mode 100644 docs/data/date-pickers/time-picker/FormPropsTimePickers.tsx create mode 100644 docs/data/date-pickers/time-picker/LocalizedTimePicker.js create mode 100644 docs/data/date-pickers/time-picker/LocalizedTimePicker.tsx create mode 100644 docs/data/date-pickers/time-picker/ResponsiveTimePickers.js create mode 100644 docs/data/date-pickers/time-picker/ResponsiveTimePickers.tsx create mode 100644 docs/data/date-pickers/time-picker/SecondsTimePicker.js create mode 100644 docs/data/date-pickers/time-picker/SecondsTimePicker.tsx create mode 100644 docs/data/date-pickers/time-picker/StaticTimePickerDemo.js create mode 100644 docs/data/date-pickers/time-picker/StaticTimePickerDemo.tsx create mode 100644 docs/data/date-pickers/time-picker/StaticTimePickerDemo.tsx.preview create mode 100644 docs/data/date-pickers/time-picker/StaticTimePickerLandscape.js create mode 100644 docs/data/date-pickers/time-picker/StaticTimePickerLandscape.tsx create mode 100644 docs/data/date-pickers/time-picker/StaticTimePickerLandscape.tsx.preview create mode 100644 docs/data/date-pickers/time-picker/SubComponentsTimePickers.js create mode 100644 docs/data/date-pickers/time-picker/SubComponentsTimePickers.tsx create mode 100644 docs/data/date-pickers/time-picker/SubComponentsTimePickers.tsx.preview create mode 100644 docs/data/date-pickers/time-picker/TimeValidationTimePicker.js create mode 100644 docs/data/date-pickers/time-picker/TimeValidationTimePicker.tsx create mode 100644 docs/data/date-pickers/time-picker/time-picker-pt.md create mode 100644 docs/data/date-pickers/time-picker/time-picker-zh.md create mode 100644 docs/data/date-pickers/time-picker/time-picker.md create mode 100644 docs/data/date-pickers/time-range-picker/time-range-picker.md create mode 100644 docs/data/pages.ts create mode 100644 docs/next-env.d.ts create mode 100644 docs/next.config.js create mode 100644 docs/pages/_app.js create mode 100644 docs/pages/_document.js create mode 100644 docs/pages/playground.example.tsx create mode 100644 docs/pages/x/advanced-components/index.js create mode 100644 docs/pages/x/api/data-grid/data-grid-premium.js create mode 100644 docs/pages/x/api/data-grid/data-grid-premium.json create mode 100644 docs/pages/x/api/data-grid/data-grid-pro.js create mode 100644 docs/pages/x/api/data-grid/data-grid-pro.json create mode 100644 docs/pages/x/api/data-grid/data-grid.js create mode 100644 docs/pages/x/api/data-grid/data-grid.json create mode 100644 docs/pages/x/api/data-grid/grid-api.js create mode 100644 docs/pages/x/api/data-grid/grid-api.md create mode 100644 docs/pages/x/api/data-grid/grid-cell-params.js create mode 100644 docs/pages/x/api/data-grid/grid-cell-params.md create mode 100644 docs/pages/x/api/data-grid/grid-col-def.js create mode 100644 docs/pages/x/api/data-grid/grid-col-def.md create mode 100644 docs/pages/x/api/data-grid/grid-column-pinning-api.json create mode 100644 docs/pages/x/api/data-grid/grid-column-pinning-state.js create mode 100644 docs/pages/x/api/data-grid/grid-column-pinning-state.md create mode 100644 docs/pages/x/api/data-grid/grid-csv-export-api.json create mode 100644 docs/pages/x/api/data-grid/grid-csv-export-options.js create mode 100644 docs/pages/x/api/data-grid/grid-csv-export-options.md create mode 100644 docs/pages/x/api/data-grid/grid-detail-panel-api.json create mode 100644 docs/pages/x/api/data-grid/grid-disable-virtualization-api.json create mode 100644 docs/pages/x/api/data-grid/grid-editing-api.json create mode 100644 docs/pages/x/api/data-grid/grid-excel-export-api.json create mode 100644 docs/pages/x/api/data-grid/grid-excel-export-options.js create mode 100644 docs/pages/x/api/data-grid/grid-excel-export-options.md create mode 100644 docs/pages/x/api/data-grid/grid-filter-api.json create mode 100644 docs/pages/x/api/data-grid/grid-filter-form.js create mode 100644 docs/pages/x/api/data-grid/grid-filter-form.json create mode 100644 docs/pages/x/api/data-grid/grid-filter-item.js create mode 100644 docs/pages/x/api/data-grid/grid-filter-item.md create mode 100644 docs/pages/x/api/data-grid/grid-filter-model.js create mode 100644 docs/pages/x/api/data-grid/grid-filter-model.md create mode 100644 docs/pages/x/api/data-grid/grid-filter-operator.js create mode 100644 docs/pages/x/api/data-grid/grid-filter-operator.md create mode 100644 docs/pages/x/api/data-grid/grid-filter-panel.js create mode 100644 docs/pages/x/api/data-grid/grid-filter-panel.json create mode 100644 docs/pages/x/api/data-grid/grid-new-editing-api.json create mode 100644 docs/pages/x/api/data-grid/grid-old-editing-api.json create mode 100644 docs/pages/x/api/data-grid/grid-pagination-api.json create mode 100644 docs/pages/x/api/data-grid/grid-print-export-api.json create mode 100644 docs/pages/x/api/data-grid/grid-print-export-options.js create mode 100644 docs/pages/x/api/data-grid/grid-print-export-options.md create mode 100644 docs/pages/x/api/data-grid/grid-row-class-name-params.js create mode 100644 docs/pages/x/api/data-grid/grid-row-class-name-params.md create mode 100644 docs/pages/x/api/data-grid/grid-row-grouping-api.json create mode 100644 docs/pages/x/api/data-grid/grid-row-params.js create mode 100644 docs/pages/x/api/data-grid/grid-row-params.md create mode 100644 docs/pages/x/api/data-grid/grid-row-spacing-params.js create mode 100644 docs/pages/x/api/data-grid/grid-row-spacing-params.md create mode 100644 docs/pages/x/api/data-grid/grid-scroll-api.json create mode 100644 docs/pages/x/api/data-grid/grid-selection-api.json create mode 100644 docs/pages/x/api/data-grid/grid-sort-api.json create mode 100644 docs/pages/x/api/data-grid/index.js create mode 100644 docs/pages/x/api/data-grid/index.md create mode 100644 docs/pages/x/api/data-grid/selectors.json create mode 100644 docs/pages/x/api/date-pickers/calendar-picker-skeleton.js create mode 100644 docs/pages/x/api/date-pickers/calendar-picker-skeleton.json create mode 100644 docs/pages/x/api/date-pickers/calendar-picker.js create mode 100644 docs/pages/x/api/date-pickers/calendar-picker.json create mode 100644 docs/pages/x/api/date-pickers/clock-picker.js create mode 100644 docs/pages/x/api/date-pickers/clock-picker.json create mode 100644 docs/pages/x/api/date-pickers/date-picker.js create mode 100644 docs/pages/x/api/date-pickers/date-picker.json create mode 100644 docs/pages/x/api/date-pickers/date-range-picker-day.js create mode 100644 docs/pages/x/api/date-pickers/date-range-picker-day.json create mode 100644 docs/pages/x/api/date-pickers/date-range-picker.js create mode 100644 docs/pages/x/api/date-pickers/date-range-picker.json create mode 100644 docs/pages/x/api/date-pickers/date-time-picker.js create mode 100644 docs/pages/x/api/date-pickers/date-time-picker.json create mode 100644 docs/pages/x/api/date-pickers/desktop-date-picker.js create mode 100644 docs/pages/x/api/date-pickers/desktop-date-picker.json create mode 100644 docs/pages/x/api/date-pickers/desktop-date-range-picker.js create mode 100644 docs/pages/x/api/date-pickers/desktop-date-range-picker.json create mode 100644 docs/pages/x/api/date-pickers/desktop-date-time-picker.js create mode 100644 docs/pages/x/api/date-pickers/desktop-date-time-picker.json create mode 100644 docs/pages/x/api/date-pickers/desktop-time-picker.js create mode 100644 docs/pages/x/api/date-pickers/desktop-time-picker.json create mode 100644 docs/pages/x/api/date-pickers/index.js create mode 100644 docs/pages/x/api/date-pickers/index.md create mode 100644 docs/pages/x/api/date-pickers/localization-provider.js create mode 100644 docs/pages/x/api/date-pickers/localization-provider.json create mode 100644 docs/pages/x/api/date-pickers/mobile-date-picker.js create mode 100644 docs/pages/x/api/date-pickers/mobile-date-picker.json create mode 100644 docs/pages/x/api/date-pickers/mobile-date-range-picker.js create mode 100644 docs/pages/x/api/date-pickers/mobile-date-range-picker.json create mode 100644 docs/pages/x/api/date-pickers/mobile-date-time-picker.js create mode 100644 docs/pages/x/api/date-pickers/mobile-date-time-picker.json create mode 100644 docs/pages/x/api/date-pickers/mobile-time-picker.js create mode 100644 docs/pages/x/api/date-pickers/mobile-time-picker.json create mode 100644 docs/pages/x/api/date-pickers/month-picker.js create mode 100644 docs/pages/x/api/date-pickers/month-picker.json create mode 100644 docs/pages/x/api/date-pickers/picker-static-wrapper.js create mode 100644 docs/pages/x/api/date-pickers/picker-static-wrapper.json create mode 100644 docs/pages/x/api/date-pickers/pickers-day.js create mode 100644 docs/pages/x/api/date-pickers/pickers-day.json create mode 100644 docs/pages/x/api/date-pickers/static-date-picker.js create mode 100644 docs/pages/x/api/date-pickers/static-date-picker.json create mode 100644 docs/pages/x/api/date-pickers/static-date-range-picker.js create mode 100644 docs/pages/x/api/date-pickers/static-date-range-picker.json create mode 100644 docs/pages/x/api/date-pickers/static-date-time-picker.js create mode 100644 docs/pages/x/api/date-pickers/static-date-time-picker.json create mode 100644 docs/pages/x/api/date-pickers/static-time-picker.js create mode 100644 docs/pages/x/api/date-pickers/static-time-picker.json create mode 100644 docs/pages/x/api/date-pickers/time-picker.js create mode 100644 docs/pages/x/api/date-pickers/time-picker.json create mode 100644 docs/pages/x/api/date-pickers/year-picker.js create mode 100644 docs/pages/x/api/date-pickers/year-picker.json create mode 100644 docs/pages/x/react-data-grid/accessibility.js create mode 100644 docs/pages/x/react-data-grid/aggregation.js create mode 100644 docs/pages/x/react-data-grid/column-definition.js create mode 100644 docs/pages/x/react-data-grid/column-dimensions.js create mode 100644 docs/pages/x/react-data-grid/column-groups.js create mode 100644 docs/pages/x/react-data-grid/column-header.js create mode 100644 docs/pages/x/react-data-grid/column-ordering.js create mode 100644 docs/pages/x/react-data-grid/column-pinning.js create mode 100644 docs/pages/x/react-data-grid/column-spanning.js create mode 100644 docs/pages/x/react-data-grid/column-visibility.js create mode 100644 docs/pages/x/react-data-grid/components.js create mode 100644 docs/pages/x/react-data-grid/demo.js create mode 100644 docs/pages/x/react-data-grid/editing-legacy.js create mode 100644 docs/pages/x/react-data-grid/editing.js create mode 100644 docs/pages/x/react-data-grid/events.js create mode 100644 docs/pages/x/react-data-grid/export.js create mode 100644 docs/pages/x/react-data-grid/filtering.js create mode 100644 docs/pages/x/react-data-grid/getting-started.js create mode 100644 docs/pages/x/react-data-grid/index.js create mode 100644 docs/pages/x/react-data-grid/layout.js create mode 100644 docs/pages/x/react-data-grid/localization.js create mode 100644 docs/pages/x/react-data-grid/master-detail.js create mode 100644 docs/pages/x/react-data-grid/migration-v4.js create mode 100644 docs/pages/x/react-data-grid/pagination.js create mode 100644 docs/pages/x/react-data-grid/pivoting.js create mode 100644 docs/pages/x/react-data-grid/row-grouping.js create mode 100644 docs/pages/x/react-data-grid/rows.js create mode 100644 docs/pages/x/react-data-grid/scrolling.js create mode 100644 docs/pages/x/react-data-grid/selection.js create mode 100644 docs/pages/x/react-data-grid/sorting.js create mode 100644 docs/pages/x/react-data-grid/state.js create mode 100644 docs/pages/x/react-data-grid/style.js create mode 100644 docs/pages/x/react-data-grid/tree-data.js create mode 100644 docs/pages/x/react-data-grid/virtualization.js create mode 100644 docs/pages/x/react-date-pickers/date-picker.js create mode 100644 docs/pages/x/react-date-pickers/date-range-picker.js create mode 100644 docs/pages/x/react-date-pickers/date-time-picker.js create mode 100644 docs/pages/x/react-date-pickers/date-time-range-picker.js create mode 100644 docs/pages/x/react-date-pickers/getting-started.js create mode 100644 docs/pages/x/react-date-pickers/localization.js create mode 100644 docs/pages/x/react-date-pickers/migration-lab.js create mode 100644 docs/pages/x/react-date-pickers/time-picker.js create mode 100644 docs/pages/x/react-date-pickers/time-range-picker.js create mode 100644 docs/public/_headers create mode 100644 docs/public/_redirects create mode 100755 docs/public/favicon.ico create mode 100644 docs/public/robots.txt create mode 100644 docs/public/static/ads-in-house/figma.png create mode 100644 docs/public/static/ads-in-house/scaffoldhub.png create mode 100644 docs/public/static/ads-in-house/themes-2.jpg create mode 100644 docs/public/static/ads-in-house/themes.png create mode 100644 docs/public/static/ads-in-house/tidelift.png create mode 100644 docs/public/static/favicon.ico create mode 100644 docs/public/static/fonts/IBMPlexSans-Bold.ttf create mode 100644 docs/public/static/fonts/IBMPlexSans-Bold.woff create mode 100644 docs/public/static/fonts/IBMPlexSans-Bold.woff2 create mode 100644 docs/public/static/fonts/IBMPlexSans-Medium.ttf create mode 100644 docs/public/static/fonts/IBMPlexSans-Medium.woff create mode 100644 docs/public/static/fonts/IBMPlexSans-Medium.woff2 create mode 100644 docs/public/static/fonts/IBMPlexSans-Regular.ttf create mode 100644 docs/public/static/fonts/IBMPlexSans-Regular.woff create mode 100644 docs/public/static/fonts/IBMPlexSans-Regular.woff2 create mode 100644 docs/public/static/fonts/IBMPlexSans-SemiBold.ttf create mode 100644 docs/public/static/fonts/IBMPlexSans-SemiBold.woff create mode 100644 docs/public/static/fonts/IBMPlexSans-SemiBold.woff2 create mode 100644 docs/public/static/fonts/PlusJakartaSans-Bold-subset.woff2 create mode 100644 docs/public/static/fonts/PlusJakartaSans-ExtraBold-subset.woff2 create mode 100644 docs/public/static/icons/180x180.png create mode 100644 docs/public/static/icons/192x192.png create mode 100644 docs/public/static/icons/256x256.png create mode 100644 docs/public/static/icons/384x384.png create mode 100644 docs/public/static/icons/48x48.png create mode 100644 docs/public/static/icons/512x512.png create mode 100644 docs/public/static/icons/96x96.png create mode 100644 docs/public/static/logo.png create mode 100644 docs/public/static/logo.svg create mode 100644 docs/public/static/logo_raw.svg create mode 100644 docs/public/static/manifest.json create mode 100644 docs/public/static/sponsors/doit-square.svg create mode 100644 docs/public/static/sponsors/doit.svg create mode 100644 docs/public/static/sponsors/octopus-dark.svg create mode 100644 docs/public/static/sponsors/octopus-light.svg create mode 100644 docs/public/static/sponsors/octopus.svg create mode 100644 docs/public/static/sponsors/tidelift.svg create mode 100644 docs/public/static/styles/prism-okaidia.css create mode 100644 docs/public/static/x/commercial-header-icon-dark.png create mode 100644 docs/public/static/x/commercial-header-icon-light.png create mode 100644 docs/public/static/x/premium.svg create mode 100644 docs/public/static/x/pro.svg create mode 100644 docs/public/static/x/watermark-dark.png create mode 100644 docs/public/static/x/watermark-light.png create mode 100644 docs/scripts/api/buildApi.ts create mode 100644 docs/scripts/api/buildComponentsDocumentation.ts create mode 100644 docs/scripts/api/buildExportsDocumentation.ts create mode 100644 docs/scripts/api/buildGridEventsDocumentation.ts create mode 100644 docs/scripts/api/buildGridSelectorsDocumentation.ts create mode 100644 docs/scripts/api/buildInterfacesDocumentation.ts create mode 100644 docs/scripts/api/utils.ts create mode 100644 docs/scripts/buildIcons.js create mode 100644 docs/scripts/buildServiceWorker.js create mode 100644 docs/scripts/formattedTSDemos.js create mode 100644 docs/scripts/generateProptypes.ts create mode 100644 docs/scripts/getTypeScriptProjects.ts create mode 100644 docs/scripts/helpers.js create mode 100644 docs/scripts/i18n.js create mode 100644 docs/scripts/tsconfig.json create mode 100644 docs/scripts/utils.ts create mode 100644 docs/src/modules/components/ApiDocs.js create mode 100644 docs/src/modules/components/ApiPage.js create mode 100644 docs/src/modules/components/SelectorsDocs.js create mode 100644 docs/src/modules/utils/babel-plugin-jsx-preview.js create mode 100644 docs/src/modules/utils/find.js create mode 100644 docs/src/modules/utils/findPages.js create mode 100644 docs/src/sw.js create mode 100644 docs/translations/api-docs/data-grid/data-grid-premium-pt.json create mode 100644 docs/translations/api-docs/data-grid/data-grid-premium-zh.json create mode 100644 docs/translations/api-docs/data-grid/data-grid-premium.json create mode 100644 docs/translations/api-docs/data-grid/data-grid-pro-pt.json create mode 100644 docs/translations/api-docs/data-grid/data-grid-pro-zh.json create mode 100644 docs/translations/api-docs/data-grid/data-grid-pro.json create mode 100644 docs/translations/api-docs/data-grid/data-grid-pt.json create mode 100644 docs/translations/api-docs/data-grid/data-grid-zh.json create mode 100644 docs/translations/api-docs/data-grid/data-grid.json create mode 100644 docs/translations/api-docs/data-grid/grid-filter-form-pt.json create mode 100644 docs/translations/api-docs/data-grid/grid-filter-form-zh.json create mode 100644 docs/translations/api-docs/data-grid/grid-filter-form.json create mode 100644 docs/translations/api-docs/data-grid/grid-filter-panel-pt.json create mode 100644 docs/translations/api-docs/data-grid/grid-filter-panel-zh.json create mode 100644 docs/translations/api-docs/data-grid/grid-filter-panel.json create mode 100644 docs/translations/api-docs/date-pickers/calendar-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/calendar-picker-skeleton-pt.json create mode 100644 docs/translations/api-docs/date-pickers/calendar-picker-skeleton-zh.json create mode 100644 docs/translations/api-docs/date-pickers/calendar-picker-skeleton.json create mode 100644 docs/translations/api-docs/date-pickers/calendar-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/calendar-picker.json create mode 100644 docs/translations/api-docs/date-pickers/clock-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/clock-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/clock-picker.json create mode 100644 docs/translations/api-docs/date-pickers/date-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/date-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/date-picker.json create mode 100644 docs/translations/api-docs/date-pickers/date-range-picker-day-pt.json create mode 100644 docs/translations/api-docs/date-pickers/date-range-picker-day-zh.json create mode 100644 docs/translations/api-docs/date-pickers/date-range-picker-day.json create mode 100644 docs/translations/api-docs/date-pickers/date-range-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/date-range-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/date-range-picker.json create mode 100644 docs/translations/api-docs/date-pickers/date-time-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/date-time-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/date-time-picker.json create mode 100644 docs/translations/api-docs/date-pickers/desktop-date-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/desktop-date-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/desktop-date-picker.json create mode 100644 docs/translations/api-docs/date-pickers/desktop-date-range-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/desktop-date-range-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/desktop-date-range-picker.json create mode 100644 docs/translations/api-docs/date-pickers/desktop-date-time-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/desktop-date-time-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/desktop-date-time-picker.json create mode 100644 docs/translations/api-docs/date-pickers/desktop-time-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/desktop-time-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/desktop-time-picker.json create mode 100644 docs/translations/api-docs/date-pickers/localization-provider-pt.json create mode 100644 docs/translations/api-docs/date-pickers/localization-provider-zh.json create mode 100644 docs/translations/api-docs/date-pickers/localization-provider.json create mode 100644 docs/translations/api-docs/date-pickers/mobile-date-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/mobile-date-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/mobile-date-picker.json create mode 100644 docs/translations/api-docs/date-pickers/mobile-date-range-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/mobile-date-range-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/mobile-date-range-picker.json create mode 100644 docs/translations/api-docs/date-pickers/mobile-date-time-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/mobile-date-time-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/mobile-date-time-picker.json create mode 100644 docs/translations/api-docs/date-pickers/mobile-time-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/mobile-time-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/mobile-time-picker.json create mode 100644 docs/translations/api-docs/date-pickers/month-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/month-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/month-picker.json create mode 100644 docs/translations/api-docs/date-pickers/picker-static-wrapper-pt.json create mode 100644 docs/translations/api-docs/date-pickers/picker-static-wrapper-zh.json create mode 100644 docs/translations/api-docs/date-pickers/picker-static-wrapper.json create mode 100644 docs/translations/api-docs/date-pickers/pickers-day-pt.json create mode 100644 docs/translations/api-docs/date-pickers/pickers-day-zh.json create mode 100644 docs/translations/api-docs/date-pickers/pickers-day.json create mode 100644 docs/translations/api-docs/date-pickers/static-date-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/static-date-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/static-date-picker.json create mode 100644 docs/translations/api-docs/date-pickers/static-date-range-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/static-date-range-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/static-date-range-picker.json create mode 100644 docs/translations/api-docs/date-pickers/static-date-time-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/static-date-time-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/static-date-time-picker.json create mode 100644 docs/translations/api-docs/date-pickers/static-time-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/static-time-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/static-time-picker.json create mode 100644 docs/translations/api-docs/date-pickers/time-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/time-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/time-picker.json create mode 100644 docs/translations/api-docs/date-pickers/year-picker-pt.json create mode 100644 docs/translations/api-docs/date-pickers/year-picker-zh.json create mode 100644 docs/translations/api-docs/date-pickers/year-picker.json create mode 100644 docs/translations/translations-zh.json create mode 100644 docs/translations/translations.json create mode 100644 docs/tsconfig.json create mode 100644 docs/tslint.json diff --git a/docs-old/README.md b/docs-old/README.md new file mode 100644 index 00000000000..46f6e96e565 --- /dev/null +++ b/docs-old/README.md @@ -0,0 +1,10 @@ +# MUI Toolpad + +Low code tool, for developers, powered by MUI. + +## Table of contents + +- [Setup instructions](./setup.md) +- [Getting started](./getting-started.md) +- Datasources + - [Google Sheets](./google-sheets.md) diff --git a/docs/data/commodity-100.json b/docs-old/data/commodity-100.json similarity index 100% rename from docs/data/commodity-100.json rename to docs-old/data/commodity-100.json diff --git a/docs/getting-started.md b/docs-old/getting-started.md similarity index 100% rename from docs/getting-started.md rename to docs-old/getting-started.md diff --git a/docs/google-sheets.md b/docs-old/google-sheets.md similarity index 100% rename from docs/google-sheets.md rename to docs-old/google-sheets.md diff --git a/docs/images/add-textfield.png b/docs-old/images/add-textfield.png similarity index 100% rename from docs/images/add-textfield.png rename to docs-old/images/add-textfield.png diff --git a/docs/images/apps-overview.png b/docs-old/images/apps-overview.png similarity index 100% rename from docs/images/apps-overview.png rename to docs-old/images/apps-overview.png diff --git a/docs/images/bind-query-state.png b/docs-old/images/bind-query-state.png similarity index 100% rename from docs/images/bind-query-state.png rename to docs-old/images/bind-query-state.png diff --git a/docs/images/create-api.png b/docs-old/images/create-api.png similarity index 100% rename from docs/images/create-api.png rename to docs-old/images/create-api.png diff --git a/docs/images/create-connection.png b/docs-old/images/create-connection.png similarity index 100% rename from docs/images/create-connection.png rename to docs-old/images/create-connection.png diff --git a/docs/images/create-state.png b/docs-old/images/create-state.png similarity index 100% rename from docs/images/create-state.png rename to docs-old/images/create-state.png diff --git a/docs/images/editor-overview.png b/docs-old/images/editor-overview.png similarity index 100% rename from docs/images/editor-overview.png rename to docs-old/images/editor-overview.png diff --git a/docs/images/page-editor.png b/docs-old/images/page-editor.png similarity index 100% rename from docs/images/page-editor.png rename to docs-old/images/page-editor.png diff --git a/docs/images/release.png b/docs-old/images/release.png similarity index 100% rename from docs/images/release.png rename to docs-old/images/release.png diff --git a/docs/images/result.png b/docs-old/images/result.png similarity index 100% rename from docs/images/result.png rename to docs-old/images/result.png diff --git a/docs/images/updated-binding.png b/docs-old/images/updated-binding.png similarity index 100% rename from docs/images/updated-binding.png rename to docs-old/images/updated-binding.png diff --git a/docs-old/package.json b/docs-old/package.json new file mode 100644 index 00000000000..313e681922f --- /dev/null +++ b/docs-old/package.json @@ -0,0 +1,5 @@ +{ + "name": "docs-old", + "version": "0.0.5-alpha.4", + "private": true +} diff --git a/docs/setup.md b/docs-old/setup.md similarity index 100% rename from docs/setup.md rename to docs-old/setup.md diff --git a/docs-old/src/modules/constants.js b/docs-old/src/modules/constants.js new file mode 100644 index 00000000000..88e4ef00505 --- /dev/null +++ b/docs-old/src/modules/constants.js @@ -0,0 +1 @@ +module.exports = require('@mui/monorepo/docs/src/modules/constants'); diff --git a/docs/README.md b/docs/README.md index 46f6e96e565..117f756b5e1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,10 +1,23 @@ -# MUI Toolpad +# MUI X docs -Low code tool, for developers, powered by MUI. +This is the documentation website of MUI X. -## Table of contents +To start the docs site in development mode, from the project root, run: -- [Setup instructions](./setup.md) -- [Getting started](./getting-started.md) -- Datasources - - [Google Sheets](./google-sheets.md) +```sh +yarn && yarn docs:dev +``` + +If you do not have yarn installed, select your OS and follow the instructions on the [Yarn website](https://yarnpkg.com/lang/en/docs/install/#mac-stable). + +_DO NOT USE NPM, use Yarn to install the dependencies._ + +## How can I add a new demo to the documentation? + +[You can follow this guide](https://github.com/mui/material-ui/blob/HEAD/CONTRIBUTING.md) +on how to get started contributing to MUI. + +## How do I help to improve the translations? + +Please visit https://translate.mui.com/ where you will be able to select a language and edit the translations. +Please don't submit pull requests directly. diff --git a/docs/babel.config.js b/docs/babel.config.js new file mode 100644 index 00000000000..773d6cb2451 --- /dev/null +++ b/docs/babel.config.js @@ -0,0 +1,75 @@ +const bpmr = require('babel-plugin-module-resolver'); +const fse = require('fs-extra'); + +function resolvePath(sourcePath, currentFile, opts) { + if (sourcePath === 'markdown') { + const base = currentFile.substring(__dirname.length).slice(0, -3); + return `${__dirname}/docs/src/${base}/`; + } + + return bpmr.resolvePath(sourcePath, currentFile, opts); +} + +const alias = { + '@mui/x-data-grid': '../packages/grid/x-data-grid/src', + '@mui/x-data-grid-generator': '../packages/grid/x-data-grid-generator/src', + '@mui/x-data-grid-pro': '../packages/grid/x-data-grid-pro/src', + '@mui/x-data-grid-premium': '../packages/grid/x-data-grid-premium/src', + '@mui/x-date-pickers': '../packages/x-date-pickers/src', + '@mui/x-date-pickers-pro': '../packages/x-date-pickers-pro/src', + '@mui/x-license-pro': '../packages/x-license-pro/src', + '@mui/docs': '../node_modules/@mui/monorepo/packages/mui-docs/src', + '@mui/markdown': '../node_modules/@mui/monorepo/docs/packages/markdown', + '@mui/monorepo': '../node_modules/@mui/monorepo', + '@mui/joy': '../node_modules/@mui/monorepo/packages/mui-joy/src', + docs: '../node_modules/@mui/monorepo/docs', // TODO remove + docsx: './', +}; + +const { version: transformRuntimeVersion } = fse.readJSONSync( + require.resolve('@babel/runtime-corejs2/package.json'), +); + +module.exports = { + presets: [ + // backport of https://github.com/zeit/next.js/pull/9511 + ['next/babel', { 'transform-runtime': { corejs: 2, version: transformRuntimeVersion } }], + ], + plugins: [ + [ + 'babel-plugin-transform-rename-import', + { + replacements: [{ original: '@mui/utils/macros/MuiError.macro', replacement: 'react' }], + }, + ], + 'babel-plugin-optimize-clsx', + // for IE 11 support + '@babel/plugin-transform-object-assign', + 'babel-plugin-preval', + [ + 'babel-plugin-module-resolver', + { + alias, + transformFunctions: ['require', 'require.context'], + resolvePath, + }, + ], + ], + ignore: [ + // Fix a Windows issue. + /@babel[\\|/]runtime/, + // Fix const foo = /{{(.+?)}}/gs; crashing. + /prettier/, + /@mui[\\|/]docs[\\|/]markdown/, + ], + env: { + production: { + plugins: [ + // TODO fix useGridSelector side effect and enable back. + // '@babel/plugin-transform-react-constant-elements', + ['babel-plugin-react-remove-properties', { properties: ['data-mui-test'] }], + ['babel-plugin-transform-react-remove-prop-types', { mode: 'remove' }], + ], + }, + }, +}; diff --git a/docs/data/advanced-components/overview.md b/docs/data/advanced-components/overview.md new file mode 100644 index 00000000000..8d34cb18aed --- /dev/null +++ b/docs/data/advanced-components/overview.md @@ -0,0 +1,186 @@ +--- +title: MUI X - Overview +--- + +# MUI X - Overview + +

MUI X is a collection of advanced UI components for complex use cases.

+ +## Licenses + +> While [MUI Core](/core/) is entirely licensed under MIT, [MUI X](/x/) serves a part of its components as MIT and the rest under a commercial license. +> You will need to purchase a license to access features that are only available with the Pro and Premium Plans. +> See [Pricing](https://mui.com/pricing/) for details. + +### MIT vs. commercial + +_How do we decide if a feature is MIT or commercial?_ + +We have been building MIT React components since 2014, +and have learned much about the strengths and weaknesses of the MIT license model. +The health of this model is improving every day. +As the community grows, it increases the probability that developers contribute improvements to the project. +You can find our pledge to nurture the MIT licensed content on [this Stewardship page](https://mui-org.notion.site/Stewardship-542a2226043d4f4a96dfb429d16cf5bd). + +However, we believe that we have reached the sustainability limits of what the model can support for advancing our mission forward. +We have seen too many MIT licensed components moving slowly or getting abandoned. +The community isn't contributing improvements as fast as the problems deserved to be solved. + +We are using a commercial license to forward the development of the most advanced features, where the MIT model can't sustain it. +A feature should only be commercial if it has no great MIT alternatives. + +The detailed feature comparison is available on the [Pricing](https://mui.com/pricing/) page. + +### Community Plan + +MUI X's Community Plan is published under [MIT license](https://tldrlegal.com/license/mit-license) and [free forever](https://mui-org.notion.site/Stewardship-542a2226043d4f4a96dfb429d16cf5bd#20f609acab4441cf9346614119fbbac1). +This plan contains features we believe are sustainable by the contributions of the open-source community. + +Community Plan packages: + +- [`@mui/x-data-grid`](https://www.npmjs.com/package/@mui/x-data-grid) +- [`@mui/x-date-pickers`](https://www.npmjs.com/package/@mui/x-date-pickers) + +### Pro Plan + +The MUI X Pro Plan expands on the limitations of the Community Plan with more advanced features such as multi-filtering, multi-sorting, column resizing and column pinning. + +The Pro Plan is available under a commercial license—visit the [Pricing](https://mui.com/pricing/) page for details. +This plan contains the features that are at the limit of what the open-source model can sustain. +For instance, providing support for handling massive amounts of data, in a flexible data grid integrated with a comprehensive set of components. + +Pro Plan packages: + +- [`@mui/x-data-grid-pro`](https://www.npmjs.com/package/@mui/x-data-grid-pro) +- [`@mui/x-date-pickers-pro`](https://www.npmjs.com/package/@mui/x-date-pickers-pro) + +The features exclusive to the Pro Plan are marked with the icon across our documentation. + +
+ +
+
+ +
+ +### Premium Plan + +The MUI X Premium Plan contains the most advanced features such as Row grouping, Excel export, Aggregation (🚧), as well as everything that's included in the Pro Plan. + +The Premium Plan is available under a commercial license—visit the [Pricing](https://mui.com/pricing/) page for details. +This plan contains highly complex features that can be useful to analyze and group data without the use of an external application. +The price of the plan targets small to medium-size teams. + +Premium Plan package: + +- [`@mui/x-data-grid-premium`](https://www.npmjs.com/package/@mui/x-data-grid-premium) + +The features exclusive to the Premium Plan are marked with the icon across our documentation. + +## Evaluation (trial) licenses + +In accordance with our [End User License Agreement](https://mui.com/store/legal/mui-x-eula/#evaluation-trial-licenses), you can use the Pro and Premium components without a commercial license for 30 days without restrictions. +You do not need to contact us to use these components for evaluation purposes. + +You will need to purchase a commercial license in order to remove the watermarks and console warnings, or after the given 30 days period of evaluation. + +## License key installation + +When you purchase a commercial license, you'll receive a license key by email. +This key removes all watermarks and console warnings. + +```jsx +import { LicenseInfo } from '@mui/x-license-pro'; + +LicenseInfo.setLicenseKey('YOUR_LICENSE_KEY'); +``` + +### Where to install the key? + +You must call `setLicenseKey` before React renders the first component. +You only need to install the key once in your application. + +### Does each developer need its own key? + +No. The license key is meant to help you get compliant with the [EULA](https://mui.com/store/legal/mui-x-eula/) of the commercial licenses. +While each developer needs to be licensed, the license key is set once, where the components are used. + +### Security + +The license key is checked without making any network requests—it's designed to be public. +In fact, it's expected for the license key to be exposed in a JavaScript bundle. +We just ask our licensed users not to publicize their license keys. + +### Validation errors + +If the validation of the license key fails, the component displays a watermark and provides a console warning in both development and production. +End users can still use the component. + +Here are the different possible validation errors: + +#### Missing license key + +If the license key is missing, the component will look something like this: + +
+ +
+
+ +
+ +> Note that you are still allowed to use the component for [evaluation purposes](#evaluation-trial-licenses) in this case. + +#### License key expired + +Licenses are perpetual: the license key will work forever with the current version of the software. + +But **access to updates and upgrades** is not perpetual. +An expired license key will always work with the version of the component it was licensed to cover. +But if you try to install a newer version of the component with an expired license, it will display a watermark and a console warning. +For example, if you purchase a one-year license today, you will be able to update to any version—including major versions—released in the next 12 months. +But you will not be able to install a newer version released two years from now, unless you purchase a new license to cover it. + +#### Invalid license key + +This error indicates that your license key doesn't match what was issued by MUI—this is likely a typo. + +## Support + +### GitHub + +We use GitHub issues as a bug and feature request tracker. If you think you have found a bug, or have a new feature idea, please start by making sure it hasn't already been [reported or fixed](https://github.com/mui/mui-x/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aclosed). You can search through existing issues and pull requests to see if someone has reported one similar to yours. + +[Open an issue](https://github.com/mui/mui-x/issues/new/choose) in the MUI X repo. + +### Stack Overflow + +Visit Stack Overflow to ask questions and read crowdsourced answers from expert developers in the MUI community, as well as MUI maintainers. + +[Post a question about MUI X](https://stackoverflow.com/questions/tagged/mui) on Stack Overflow. + +### Professional support + +When purchasing an MUI X Pro or Premium license you get access to professional support for a limited duration. +Support is available on multiple channels, but the recommended channels are: + +- GitHub: You can [open a new issue](https://github.com/mui/mui-x/issues/new/choose) and leave your Order ID, so we can prioritize accordingly. +- Email (**only if your issue requires sharing private information**): You can [open a new issue](https://support.mui.com/hc/en-us/requests/new?tf_360023797420=mui_x) or send an email to x@mui.com. + +Your Order ID on the issue helps us prioritize the issues based on the following support levels: + +1. **MUI X Pro**: MUI's maintainers give these issues more attention than the ones from the Community plan. +2. **MUI X Premium**: Same as MUI X Pro, but with priority over Pro. +3. **MUI X Priority support add-on (not available yet)**: A provided SLA with 24h for the first answer. + +## Roadmap + +To learn more about our plans and goals for the MUI X product line, visit our [public roadmap](https://github.com/mui/mui-x/projects/1). + +:::warning +**Disclaimer**: We operate in a dynamic environment, and things are subject to change. +The information provided is intended to outline the general framework direction, for informational purposes only. +We may decide to add or remove new items at any time, depending on our capability to deliver while meeting our quality standards. +The development, releases, and timing of any features or functionality remains at the sole discretion of MUI. +The roadmap does not represent a commitment, obligation, or promise to deliver at any time. +::: diff --git a/docs/data/data-grid/accessibility/DensitySelectorGrid.js b/docs/data/data-grid/accessibility/DensitySelectorGrid.js new file mode 100644 index 00000000000..c67ff7d1836 --- /dev/null +++ b/docs/data/data-grid/accessibility/DensitySelectorGrid.js @@ -0,0 +1,34 @@ +import * as React from 'react'; +import { + DataGrid, + GridToolbarContainer, + GridToolbarDensitySelector, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function CustomToolbar() { + return ( + + + + ); +} + +export default function DensitySelectorGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 4, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/accessibility/DensitySelectorGrid.tsx b/docs/data/data-grid/accessibility/DensitySelectorGrid.tsx new file mode 100644 index 00000000000..c67ff7d1836 --- /dev/null +++ b/docs/data/data-grid/accessibility/DensitySelectorGrid.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import { + DataGrid, + GridToolbarContainer, + GridToolbarDensitySelector, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function CustomToolbar() { + return ( + + + + ); +} + +export default function DensitySelectorGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 4, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/accessibility/DensitySelectorGrid.tsx.preview b/docs/data/data-grid/accessibility/DensitySelectorGrid.tsx.preview new file mode 100644 index 00000000000..9a7e9e10106 --- /dev/null +++ b/docs/data/data-grid/accessibility/DensitySelectorGrid.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.js b/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.js new file mode 100644 index 00000000000..75dae26d407 --- /dev/null +++ b/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.js @@ -0,0 +1,35 @@ +import * as React from 'react'; +import { + DataGrid, + GridToolbarContainer, + GridToolbarDensitySelector, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function CustomToolbar() { + return ( + + + + ); +} + +export default function DensitySelectorSmallGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 4, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx b/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx new file mode 100644 index 00000000000..75dae26d407 --- /dev/null +++ b/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import { + DataGrid, + GridToolbarContainer, + GridToolbarDensitySelector, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function CustomToolbar() { + return ( + + + + ); +} + +export default function DensitySelectorSmallGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 4, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx.preview b/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx.preview new file mode 100644 index 00000000000..0e21d941a7a --- /dev/null +++ b/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/accessibility/FocusManagement.js b/docs/data/data-grid/accessibility/FocusManagement.js new file mode 100644 index 00000000000..27b33bd9376 --- /dev/null +++ b/docs/data/data-grid/accessibility/FocusManagement.js @@ -0,0 +1,57 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { DataGrid } from '@mui/x-data-grid'; +import Box from '@mui/material/Box'; +import Link from '@mui/material/Link'; + +const CorrectRenderLink = (props) => ( + + + more info + + +); + +CorrectRenderLink.propTypes = { + /** + * the tabIndex value. + */ + tabIndex: PropTypes.oneOf([-1, 0]).isRequired, +}; + +const WrongRenderLink = () => ( + + more info + +); + +const correctColumns = [ + { field: 'link', renderCell: CorrectRenderLink, width: 200 }, +]; + +const wrongColumns = [{ field: 'link', renderCell: WrongRenderLink, width: 200 }]; + +const rows = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]; + +export default function FocusManagement() { + return ( +
+
+

Without focus management

+
+ +
+
+
+

Correct focus management

+
+ +
+
+
+ ); +} diff --git a/docs/data/data-grid/accessibility/FocusManagement.tsx b/docs/data/data-grid/accessibility/FocusManagement.tsx new file mode 100644 index 00000000000..f9d27ff9c0a --- /dev/null +++ b/docs/data/data-grid/accessibility/FocusManagement.tsx @@ -0,0 +1,55 @@ +import * as React from 'react'; +import { + DataGrid, + GridColumns, + GridRenderCellParams, + GridRowsProp, +} from '@mui/x-data-grid'; +import Box from '@mui/material/Box'; +import Link from '@mui/material/Link'; + +const CorrectRenderLink = (props: GridRenderCellParams) => ( + + + more info + + +); + +const WrongRenderLink = () => ( + + more info + +); + +const correctColumns: GridColumns = [ + { field: 'link', renderCell: CorrectRenderLink, width: 200 }, +]; +const wrongColumns: GridColumns = [ + { field: 'link', renderCell: WrongRenderLink, width: 200 }, +]; + +const rows: GridRowsProp = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]; + +export default function FocusManagement() { + return ( +
+
+

Without focus management

+
+ +
+
+
+

Correct focus management

+
+ +
+
+
+ ); +} diff --git a/docs/data/data-grid/accessibility/FocusManagement.tsx.preview b/docs/data/data-grid/accessibility/FocusManagement.tsx.preview new file mode 100644 index 00000000000..93dfbf8cf30 --- /dev/null +++ b/docs/data/data-grid/accessibility/FocusManagement.tsx.preview @@ -0,0 +1,16 @@ +
+

Without focus management

+
+ +
+
+
+

Correct focus management

+
+ +
+
\ No newline at end of file diff --git a/docs/data/data-grid/accessibility/accessibility.md b/docs/data/data-grid/accessibility/accessibility.md new file mode 100644 index 00000000000..a3f835a5157 --- /dev/null +++ b/docs/data/data-grid/accessibility/accessibility.md @@ -0,0 +1,133 @@ +--- +title: Data Grid - Accessibility +--- + +# Data Grid - Accessibility + +

The Data Grid has complete accessibility support. For instance, every cell is accessible using the keyboard.

+ +## Guidelines + +The most commonly encountered conformance guidelines for accessibility are: + +- [WCAG](https://www.w3.org/WAI/standards-guidelines/wcag/) - Globally accepted standard +- [ADA](https://www.ada.gov/) - US Department of Justice +- [Section 508](https://www.section508.gov/) - US federal agencies + +WCAG 2.0 has three levels of conformance; A, AA, and AAA (in order of conformance). +As meeting WCAG 2.0 level AA guidelines also meets the ADA and Section 508 standards, it's likely the standard that most organizations will want to target. + +The [WAI-ARIA authoring practices](https://www.w3.org/WAI/ARIA/apg/patterns/grid/) provides valuable insight on how to make the grid highly accessible. + +## Density + +You can change the density of the rows and the column header. + +### Density selector + +To enable the density selector, you need to compose a toolbar containing the `GridToolbarDensitySelector` component and apply it using the `Toolbar` property in the grid `components` prop. + +The user can change the density of the data grid by using the density selector from the toolbar. + +{{"demo": "DensitySelectorGrid.js", "bg": "inline"}} + +To hide the density selector add the `disableDensitySelector` prop to the data grid. + +### Density prop + +The vertical density of the data grid can be set using the `density` prop. +The `density` prop applies the values determined by the `rowHeight` and `headerHeight` props if supplied. +The user can override this setting with the toolbar density selector if provided. + +{{"demo": "DensitySelectorSmallGrid.js", "bg": "inline"}} + +## Keyboard navigation + +The grid responds to keyboard interactions from the user and emits events when key presses happen on the grid cells. + +### Tab sequence + +According to [WAI-ARIA](https://www.w3.org/WAI/ARIA/apg/patterns/grid/), only one of the focusable elements contained by the grid should be included in the page tab sequence. +For an element to be included in the tab sequence, it needs to have a `tabIndex` value of zero or greater. + +When a cell of the grid is focused, the first inner element with `tabIndex={0}` will receive the focus. +If there is no element with `tabIndex={0}`, the focus is set on the cell itself. + +In the example below, the first grid does not remove links from the tab sequence, which implies having to pass through all the links before accessing the pagination controls. +This behavior makes it complicated to navigate between elements when using large datasets. + +{{"demo": "FocusManagement.js", "bg": "inline", "defaultCodeOpen": false}} + +If you are customizing cell rendering with the [`renderCell`](/x/react-data-grid/column-definition/#rendering-cells) method, you become responsible for removing focusable elements from the page tab sequence. +To do so, use the `tabIndex` prop passed to the `renderCell` params to know if the rendered cell has focus and so if the inner elements should be removed from the tab sequence. + +```jsx +renderCell: (params) => ( + + + more info + + +); +``` + +### Navigation + +Use the arrow keys to move the focus. + +| Keys | Description | +| -----------------------------------------------------------------: | :---------------------------------------------------------- | +| Arrow Left | Navigate between cell elements | +| Arrow Bottom | Navigate between cell elements | +| Arrow Right | Navigate between cell elements | +| Arrow Up | Navigate between cell elements | +| Home | Navigate to the first cell of the current row | +| End | Navigate to the last cell of the current row | +| Ctrl+Home | Navigate to the first cell of the first row | +| Ctrl+End | Navigate to the last cell of the last row | +| Space | Navigate to the next scrollable page | +| Page Up | Navigate to the previous scrollable page | +| Page Down | Navigate to the next scrollable page | +| Space | Toggle row children expansion when grouping cell is focused | + +### Selection + +| Keys | Description | +| ---------------------------------------------------------------------------: | :------------------------------------------------------------------- | +| Shift+Space | Select the current row | +| Shift+Arrow Up/Down | Select the current row and the row above or below | +| Shift+ Click on cell | Select the range of rows between the first and the last clicked rows | +| Ctrl+A | Select all rows | +| Ctrl+C | Copy the currently selected row(s) | +| ALT+C | Copy the currently selected row(s) including headers | +| Ctrl+ Click on cell | Enable multi-selection | +| Ctrl+ Click on a selected row | Deselect the row | + +### Sorting + +| Keys | Description | +| -------------------------------------------------------------------: | :------------------------------------------------- | +| Ctrl+ Click on header | Enable multi-sorting | +| Shift+ Click on header | Enable multi-sorting | +| Shift+Enter | Enable multi-sorting when column header is focused | +| Enter | Sort column when column header is focused | +| Ctrl+Enter | Open column menu when column header is focused | + +### Group & pivot + +| Keys | Description | +| ------------------------------------------------------------------: | :-------------------------------- | +| Ctrl+Enter | Toggles the detail panel of a row | + +### Key assignment conventions + +The above key assignments are for Windows and Linux. +On macOS: + +- replace Ctrl with ⌘ Command +- replace ALT with ⌥ Option + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/aggregation/aggregation.md b/docs/data/data-grid/aggregation/aggregation.md new file mode 100644 index 00000000000..1aa0d80d05a --- /dev/null +++ b/docs/data/data-grid/aggregation/aggregation.md @@ -0,0 +1,20 @@ +--- +title: Data Grid - Aggregation +--- + +# Data Grid - Aggregation [](https://mui.com/store/items/mui-x-premium/) + +

Apply aggregation function to populate the group row with values.

+ +:::warning +This feature isn't implemented yet. It's coming. + +👍 Upvote [issue #213](https://github.com/mui/mui-x/issues/213) if you want to see it land faster. +::: + +When grouping, you will be able to apply an aggregation function to populate the group row with values. + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/column-definition/BasicColumnsGrid.js b/docs/data/data-grid/column-definition/BasicColumnsGrid.js new file mode 100644 index 00000000000..58c2e3a444f --- /dev/null +++ b/docs/data/data-grid/column-definition/BasicColumnsGrid.js @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +export default function BasicColumnsGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-definition/BasicColumnsGrid.tsx b/docs/data/data-grid/column-definition/BasicColumnsGrid.tsx new file mode 100644 index 00000000000..58c2e3a444f --- /dev/null +++ b/docs/data/data-grid/column-definition/BasicColumnsGrid.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +export default function BasicColumnsGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-definition/BasicColumnsGrid.tsx.preview b/docs/data/data-grid/column-definition/BasicColumnsGrid.tsx.preview new file mode 100644 index 00000000000..d1bac65ad67 --- /dev/null +++ b/docs/data/data-grid/column-definition/BasicColumnsGrid.tsx.preview @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-definition/ColumnTypesGrid.js b/docs/data/data-grid/column-definition/ColumnTypesGrid.js new file mode 100644 index 00000000000..4c04b34780b --- /dev/null +++ b/docs/data/data-grid/column-definition/ColumnTypesGrid.js @@ -0,0 +1,146 @@ +import * as React from 'react'; +import { DataGrid, GridActionsCellItem } from '@mui/x-data-grid'; +import DeleteIcon from '@mui/icons-material/Delete'; +import SecurityIcon from '@mui/icons-material/Security'; +import FileCopyIcon from '@mui/icons-material/FileCopy'; +import { randomCreatedDate, randomUpdatedDate } from '@mui/x-data-grid-generator'; + +const initialRows = [ + { + id: 1, + name: 'Damien', + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + isAdmin: true, + country: 'Spain', + discount: '', + }, + { + id: 2, + name: 'Nicolas', + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + isAdmin: false, + country: 'France', + discount: '', + }, + { + id: 3, + name: 'Kate', + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + isAdmin: false, + country: 'Brazil', + discount: 'junior', + }, +]; + +export default function ColumnTypesGrid() { + const [rows, setRows] = React.useState(initialRows); + + const deleteUser = React.useCallback( + (id) => () => { + setTimeout(() => { + setRows((prevRows) => prevRows.filter((row) => row.id !== id)); + }); + }, + [], + ); + + const toggleAdmin = React.useCallback( + (id) => () => { + setRows((prevRows) => + prevRows.map((row) => + row.id === id ? { ...row, isAdmin: !row.isAdmin } : row, + ), + ); + }, + [], + ); + + const duplicateUser = React.useCallback( + (id) => () => { + setRows((prevRows) => { + const rowToDuplicate = prevRows.find((row) => row.id === id); + return [...prevRows, { ...rowToDuplicate, id: Date.now() }]; + }); + }, + [], + ); + + const columns = React.useMemo( + () => [ + { field: 'name', type: 'string' }, + { field: 'age', type: 'number' }, + { field: 'dateCreated', type: 'date', width: 130 }, + { field: 'lastLogin', type: 'dateTime', width: 180 }, + { field: 'isAdmin', type: 'boolean', width: 120 }, + { + field: 'country', + type: 'singleSelect', + width: 120, + valueOptions: [ + 'Bulgaria', + 'Netherlands', + 'France', + 'United Kingdom', + 'Spain', + 'Brazil', + ], + }, + { + field: 'discount', + type: 'singleSelect', + width: 120, + editable: true, + valueOptions: ({ row }) => { + if (row === undefined) { + return ['EU-resident', 'junior']; + } + const options = []; + if (!['United Kingdom', 'Brazil'].includes(row.country)) { + options.push('EU-resident'); + } + if (row.age < 27) { + options.push('junior'); + } + return options; + }, + }, + { + field: 'actions', + type: 'actions', + width: 80, + getActions: (params) => [ + } + label="Delete" + onClick={deleteUser(params.id)} + />, + } + label="Toggle Admin" + onClick={toggleAdmin(params.id)} + showInMenu + />, + } + label="Duplicate User" + onClick={duplicateUser(params.id)} + showInMenu + />, + ], + }, + ], + [deleteUser, toggleAdmin, duplicateUser], + ); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-definition/ColumnTypesGrid.tsx b/docs/data/data-grid/column-definition/ColumnTypesGrid.tsx new file mode 100644 index 00000000000..3a1807c964c --- /dev/null +++ b/docs/data/data-grid/column-definition/ColumnTypesGrid.tsx @@ -0,0 +1,153 @@ +import * as React from 'react'; +import { + DataGrid, + GridActionsCellItem, + GridRowId, + GridColumns, +} from '@mui/x-data-grid'; +import DeleteIcon from '@mui/icons-material/Delete'; +import SecurityIcon from '@mui/icons-material/Security'; +import FileCopyIcon from '@mui/icons-material/FileCopy'; +import { randomCreatedDate, randomUpdatedDate } from '@mui/x-data-grid-generator'; + +const initialRows = [ + { + id: 1, + name: 'Damien', + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + isAdmin: true, + country: 'Spain', + discount: '', + }, + { + id: 2, + name: 'Nicolas', + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + isAdmin: false, + country: 'France', + discount: '', + }, + { + id: 3, + name: 'Kate', + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + isAdmin: false, + country: 'Brazil', + discount: 'junior', + }, +]; + +type Row = typeof initialRows[number]; + +export default function ColumnTypesGrid() { + const [rows, setRows] = React.useState(initialRows); + + const deleteUser = React.useCallback( + (id: GridRowId) => () => { + setTimeout(() => { + setRows((prevRows) => prevRows.filter((row) => row.id !== id)); + }); + }, + [], + ); + + const toggleAdmin = React.useCallback( + (id: GridRowId) => () => { + setRows((prevRows) => + prevRows.map((row) => + row.id === id ? { ...row, isAdmin: !row.isAdmin } : row, + ), + ); + }, + [], + ); + + const duplicateUser = React.useCallback( + (id: GridRowId) => () => { + setRows((prevRows) => { + const rowToDuplicate = prevRows.find((row) => row.id === id)!; + return [...prevRows, { ...rowToDuplicate, id: Date.now() }]; + }); + }, + [], + ); + + const columns = React.useMemo>( + () => [ + { field: 'name', type: 'string' }, + { field: 'age', type: 'number' }, + { field: 'dateCreated', type: 'date', width: 130 }, + { field: 'lastLogin', type: 'dateTime', width: 180 }, + { field: 'isAdmin', type: 'boolean', width: 120 }, + { + field: 'country', + type: 'singleSelect', + width: 120, + valueOptions: [ + 'Bulgaria', + 'Netherlands', + 'France', + 'United Kingdom', + 'Spain', + 'Brazil', + ], + }, + { + field: 'discount', + type: 'singleSelect', + width: 120, + editable: true, + valueOptions: ({ row }) => { + if (row === undefined) { + return ['EU-resident', 'junior']; + } + const options: string[] = []; + if (!['United Kingdom', 'Brazil'].includes(row.country)) { + options.push('EU-resident'); + } + if (row.age < 27) { + options.push('junior'); + } + return options; + }, + }, + { + field: 'actions', + type: 'actions', + width: 80, + getActions: (params) => [ + } + label="Delete" + onClick={deleteUser(params.id)} + />, + } + label="Toggle Admin" + onClick={toggleAdmin(params.id)} + showInMenu + />, + } + label="Duplicate User" + onClick={duplicateUser(params.id)} + showInMenu + />, + ], + }, + ], + [deleteUser, toggleAdmin, duplicateUser], + ); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-definition/ColumnTypesGrid.tsx.preview b/docs/data/data-grid/column-definition/ColumnTypesGrid.tsx.preview new file mode 100644 index 00000000000..6f326f7a9cd --- /dev/null +++ b/docs/data/data-grid/column-definition/ColumnTypesGrid.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-definition/ColumnsSelectorsNoSnap.js b/docs/data/data-grid/column-definition/ColumnsSelectorsNoSnap.js new file mode 100644 index 00000000000..666e6dfd851 --- /dev/null +++ b/docs/data/data-grid/column-definition/ColumnsSelectorsNoSnap.js @@ -0,0 +1,6 @@ +import React from 'react'; +import SelectorsDocs from 'docsx/src/modules/components/SelectorsDocs'; + +export default function ColumnsSelectorsNoSnap() { + return ; +} diff --git a/docs/data/data-grid/column-definition/CustomColumnTypesGrid.js b/docs/data/data-grid/column-definition/CustomColumnTypesGrid.js new file mode 100644 index 00000000000..fc815952bef --- /dev/null +++ b/docs/data/data-grid/column-definition/CustomColumnTypesGrid.js @@ -0,0 +1,60 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid } from '@mui/x-data-grid'; +import { randomStatusOptions, randomPrice } from '@mui/x-data-grid-generator'; + +const rows = [ + { + id: 1, + status: randomStatusOptions(), + subTotal: randomPrice(), + total: randomPrice(), + }, + { + id: 2, + status: randomStatusOptions(), + subTotal: randomPrice(), + total: randomPrice(), + }, + { + id: 3, + status: randomStatusOptions(), + subTotal: randomPrice(), + total: randomPrice(), + }, +]; + +const currencyFormatter = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', +}); + +const usdPrice = { + type: 'number', + width: 130, + valueFormatter: ({ value }) => currencyFormatter.format(Number(value)), + cellClassName: 'font-tabular-nums', +}; + +export default function CustomColumnTypesGrid() { + return ( + + + + ); +} diff --git a/docs/data/data-grid/column-definition/CustomColumnTypesGrid.tsx b/docs/data/data-grid/column-definition/CustomColumnTypesGrid.tsx new file mode 100644 index 00000000000..3d4593b047c --- /dev/null +++ b/docs/data/data-grid/column-definition/CustomColumnTypesGrid.tsx @@ -0,0 +1,60 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid, GridColTypeDef } from '@mui/x-data-grid'; +import { randomStatusOptions, randomPrice } from '@mui/x-data-grid-generator'; + +const rows = [ + { + id: 1, + status: randomStatusOptions(), + subTotal: randomPrice(), + total: randomPrice(), + }, + { + id: 2, + status: randomStatusOptions(), + subTotal: randomPrice(), + total: randomPrice(), + }, + { + id: 3, + status: randomStatusOptions(), + subTotal: randomPrice(), + total: randomPrice(), + }, +]; + +const currencyFormatter = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', +}); + +const usdPrice: GridColTypeDef = { + type: 'number', + width: 130, + valueFormatter: ({ value }) => currencyFormatter.format(Number(value)), + cellClassName: 'font-tabular-nums', +}; + +export default function CustomColumnTypesGrid() { + return ( + + + + ); +} diff --git a/docs/data/data-grid/column-definition/CustomColumnTypesGrid.tsx.preview b/docs/data/data-grid/column-definition/CustomColumnTypesGrid.tsx.preview new file mode 100644 index 00000000000..8b4c197130a --- /dev/null +++ b/docs/data/data-grid/column-definition/CustomColumnTypesGrid.tsx.preview @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-definition/RenderCellGrid.js b/docs/data/data-grid/column-definition/RenderCellGrid.js new file mode 100644 index 00000000000..2132459cc94 --- /dev/null +++ b/docs/data/data-grid/column-definition/RenderCellGrid.js @@ -0,0 +1,89 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Button from '@mui/material/Button'; + +import { DataGrid } from '@mui/x-data-grid'; + +const RenderDate = (props) => { + const { hasFocus, value } = props; + const buttonElement = React.useRef(null); + const rippleRef = React.useRef(null); + + React.useLayoutEffect(() => { + if (hasFocus) { + const input = buttonElement.current?.querySelector('input'); + input?.focus(); + } else if (rippleRef.current) { + // Only available in @mui/material v5.4.1 or later + rippleRef.current.stop({}); + } + }, [hasFocus]); + + return ( + + {value?.getFullYear() ?? ''} + + + ); +}; + +RenderDate.propTypes = { + /** + * If true, the cell is the active element. + */ + hasFocus: PropTypes.bool.isRequired, + /** + * The cell value, but if the column has valueGetter, use getValue. + */ + value: PropTypes.instanceOf(Date), +}; + +const columns = [ + { + field: 'date', + headerName: 'Year', + width: 150, + renderCell: RenderDate, + }, +]; + +const rows = [ + { + id: 1, + date: new Date(1979, 0, 1), + }, + { + id: 2, + date: new Date(1984, 1, 1), + }, + { + id: 3, + date: new Date(1992, 2, 1), + }, +]; + +export default function RenderCellGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-definition/RenderCellGrid.tsx b/docs/data/data-grid/column-definition/RenderCellGrid.tsx new file mode 100644 index 00000000000..e84355b27ec --- /dev/null +++ b/docs/data/data-grid/column-definition/RenderCellGrid.tsx @@ -0,0 +1,77 @@ +import * as React from 'react'; +import Button from '@mui/material/Button'; +import { TouchRippleActions } from '@mui/material/ButtonBase/TouchRipple'; +import { DataGrid, GridColDef, GridRenderCellParams } from '@mui/x-data-grid'; + +const RenderDate = (props: GridRenderCellParams) => { + const { hasFocus, value } = props; + const buttonElement = React.useRef(null); + const rippleRef = React.useRef(null); + + React.useLayoutEffect(() => { + if (hasFocus) { + const input = buttonElement.current?.querySelector('input'); + input?.focus(); + } else if (rippleRef.current) { + // Only available in @mui/material v5.4.1 or later + rippleRef.current.stop({} as any); + } + }, [hasFocus]); + + return ( + + {value?.getFullYear() ?? ''} + + + ); +}; + +const columns: GridColDef[] = [ + { + field: 'date', + headerName: 'Year', + width: 150, + renderCell: RenderDate, + }, +]; + +const rows = [ + { + id: 1, + date: new Date(1979, 0, 1), + }, + { + id: 2, + date: new Date(1984, 1, 1), + }, + { + id: 3, + date: new Date(1992, 2, 1), + }, +]; + +export default function RenderCellGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-definition/RenderCellGrid.tsx.preview b/docs/data/data-grid/column-definition/RenderCellGrid.tsx.preview new file mode 100644 index 00000000000..074afd47c44 --- /dev/null +++ b/docs/data/data-grid/column-definition/RenderCellGrid.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-definition/RenderExpandCellGrid.js b/docs/data/data-grid/column-definition/RenderExpandCellGrid.js new file mode 100644 index 00000000000..c27520f69a2 --- /dev/null +++ b/docs/data/data-grid/column-definition/RenderExpandCellGrid.js @@ -0,0 +1,188 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Paper from '@mui/material/Paper'; +import Popper from '@mui/material/Popper'; +import { DataGrid } from '@mui/x-data-grid'; + +function isOverflown(element) { + return ( + element.scrollHeight > element.clientHeight || + element.scrollWidth > element.clientWidth + ); +} + +const GridCellExpand = React.memo(function GridCellExpand(props) { + const { width, value } = props; + const wrapper = React.useRef(null); + const cellDiv = React.useRef(null); + const cellValue = React.useRef(null); + const [anchorEl, setAnchorEl] = React.useState(null); + const [showFullCell, setShowFullCell] = React.useState(false); + const [showPopper, setShowPopper] = React.useState(false); + + const handleMouseEnter = () => { + const isCurrentlyOverflown = isOverflown(cellValue.current); + setShowPopper(isCurrentlyOverflown); + setAnchorEl(cellDiv.current); + setShowFullCell(true); + }; + + const handleMouseLeave = () => { + setShowFullCell(false); + }; + + React.useEffect(() => { + if (!showFullCell) { + return undefined; + } + + function handleKeyDown(nativeEvent) { + // IE11, Edge (prior to using Bink?) use 'Esc' + if (nativeEvent.key === 'Escape' || nativeEvent.key === 'Esc') { + setShowFullCell(false); + } + } + + document.addEventListener('keydown', handleKeyDown); + + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [setShowFullCell, showFullCell]); + + return ( + + + + {value} + + {showPopper && ( + + + + {value} + + + + )} + + ); +}); + +GridCellExpand.propTypes = { + value: PropTypes.string.isRequired, + width: PropTypes.number.isRequired, +}; + +function renderCellExpand(params) { + return ( + + ); +} + +renderCellExpand.propTypes = { + /** + * The column of the row that the current cell belongs to. + */ + colDef: PropTypes.object.isRequired, + /** + * The cell value, but if the column has valueGetter, use getValue. + */ + value: PropTypes.string, +}; + +const columns = [ + { field: 'col1', headerName: 'Column 1', width: 80, renderCell: renderCellExpand }, + { + field: 'col2', + headerName: 'Column 2', + width: 100, + renderCell: renderCellExpand, + }, + { + field: 'col3', + headerName: 'Column 3', + width: 150, + renderCell: renderCellExpand, + }, +]; + +const rows = [ + { + id: 1, + col1: 'Hello', + col2: 'World', + col3: 'In publishing and graphic design, Lorem ipsum is a placeholder text commonly used.', + }, + { + id: 2, + col1: 'DataGridPro', + col2: 'is Awesome', + col3: 'In publishing and graphic design, Lorem ipsum is a placeholder text or a typeface without relying on meaningful content. Lorem ipsum may be used as a placeholder before final copy is available.', + }, + { + id: 3, + col1: 'MUI', + col2: 'is Amazing', + col3: 'Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document or a typeface without relying on meaningful content. Lorem ipsum may be used as a placeholder before final copy is available.', + }, + { + id: 4, + col1: 'Hello', + col2: 'World', + col3: 'In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form.', + }, + { + id: 5, + col1: 'DataGridPro', + col2: 'is Awesome', + col3: 'Typeface without relying on meaningful content. Lorem ipsum may be used as a placeholder before final copy is available.', + }, + { + id: 6, + col1: 'MUI', + col2: 'is Amazing', + col3: 'Lorem ipsum may be used as a placeholder before final copy is available.', + }, +]; + +export default function RenderExpandCellGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-definition/RenderExpandCellGrid.tsx b/docs/data/data-grid/column-definition/RenderExpandCellGrid.tsx new file mode 100644 index 00000000000..e0638a0ae93 --- /dev/null +++ b/docs/data/data-grid/column-definition/RenderExpandCellGrid.tsx @@ -0,0 +1,177 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Paper from '@mui/material/Paper'; +import Popper from '@mui/material/Popper'; +import { DataGrid, GridColDef, GridRenderCellParams } from '@mui/x-data-grid'; + +interface GridCellExpandProps { + value: string; + width: number; +} + +function isOverflown(element: Element): boolean { + return ( + element.scrollHeight > element.clientHeight || + element.scrollWidth > element.clientWidth + ); +} + +const GridCellExpand = React.memo(function GridCellExpand( + props: GridCellExpandProps, +) { + const { width, value } = props; + const wrapper = React.useRef(null); + const cellDiv = React.useRef(null); + const cellValue = React.useRef(null); + const [anchorEl, setAnchorEl] = React.useState(null); + const [showFullCell, setShowFullCell] = React.useState(false); + const [showPopper, setShowPopper] = React.useState(false); + + const handleMouseEnter = () => { + const isCurrentlyOverflown = isOverflown(cellValue.current!); + setShowPopper(isCurrentlyOverflown); + setAnchorEl(cellDiv.current); + setShowFullCell(true); + }; + + const handleMouseLeave = () => { + setShowFullCell(false); + }; + + React.useEffect(() => { + if (!showFullCell) { + return undefined; + } + + function handleKeyDown(nativeEvent: KeyboardEvent) { + // IE11, Edge (prior to using Bink?) use 'Esc' + if (nativeEvent.key === 'Escape' || nativeEvent.key === 'Esc') { + setShowFullCell(false); + } + } + + document.addEventListener('keydown', handleKeyDown); + + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [setShowFullCell, showFullCell]); + + return ( + + + + {value} + + {showPopper && ( + + + + {value} + + + + )} + + ); +}); + +function renderCellExpand(params: GridRenderCellParams) { + return ( + + ); +} + +const columns: GridColDef[] = [ + { field: 'col1', headerName: 'Column 1', width: 80, renderCell: renderCellExpand }, + { + field: 'col2', + headerName: 'Column 2', + width: 100, + renderCell: renderCellExpand, + }, + { + field: 'col3', + headerName: 'Column 3', + width: 150, + renderCell: renderCellExpand, + }, +]; +const rows: any = [ + { + id: 1, + col1: 'Hello', + col2: 'World', + col3: 'In publishing and graphic design, Lorem ipsum is a placeholder text commonly used.', + }, + { + id: 2, + col1: 'DataGridPro', + col2: 'is Awesome', + col3: 'In publishing and graphic design, Lorem ipsum is a placeholder text or a typeface without relying on meaningful content. Lorem ipsum may be used as a placeholder before final copy is available.', + }, + { + id: 3, + col1: 'MUI', + col2: 'is Amazing', + col3: 'Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document or a typeface without relying on meaningful content. Lorem ipsum may be used as a placeholder before final copy is available.', + }, + { + id: 4, + col1: 'Hello', + col2: 'World', + col3: 'In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form.', + }, + { + id: 5, + col1: 'DataGridPro', + col2: 'is Awesome', + col3: 'Typeface without relying on meaningful content. Lorem ipsum may be used as a placeholder before final copy is available.', + }, + { + id: 6, + col1: 'MUI', + col2: 'is Amazing', + col3: 'Lorem ipsum may be used as a placeholder before final copy is available.', + }, +]; + +export default function RenderExpandCellGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-definition/RenderExpandCellGrid.tsx.preview b/docs/data/data-grid/column-definition/RenderExpandCellGrid.tsx.preview new file mode 100644 index 00000000000..074afd47c44 --- /dev/null +++ b/docs/data/data-grid/column-definition/RenderExpandCellGrid.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-definition/ValueFormatterGrid.js b/docs/data/data-grid/column-definition/ValueFormatterGrid.js new file mode 100644 index 00000000000..c9dcbec204a --- /dev/null +++ b/docs/data/data-grid/column-definition/ValueFormatterGrid.js @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +const rows = [ + { + id: 1, + taxRate: 0.1, + }, + { + id: 2, + taxRate: 0.2, + }, + { + id: 3, + taxRate: 0.3, + }, +]; + +export default function ValueFormatterGrid() { + return ( +
+ { + if (params.value == null) { + return ''; + } + + const valueFormatted = Number(params.value * 100).toLocaleString(); + return `${valueFormatted} %`; + }, + }, + ]} + /> +
+ ); +} diff --git a/docs/data/data-grid/column-definition/ValueFormatterGrid.tsx b/docs/data/data-grid/column-definition/ValueFormatterGrid.tsx new file mode 100644 index 00000000000..2af23788937 --- /dev/null +++ b/docs/data/data-grid/column-definition/ValueFormatterGrid.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { DataGrid, GridValueFormatterParams } from '@mui/x-data-grid'; + +const rows = [ + { + id: 1, + taxRate: 0.1, + }, + { + id: 2, + taxRate: 0.2, + }, + { + id: 3, + taxRate: 0.3, + }, +]; + +export default function ValueFormatterGrid() { + return ( +
+ ) => { + if (params.value == null) { + return ''; + } + + const valueFormatted = Number(params.value * 100).toLocaleString(); + return `${valueFormatted} %`; + }, + }, + ]} + /> +
+ ); +} diff --git a/docs/data/data-grid/column-definition/ValueGetterGrid.js b/docs/data/data-grid/column-definition/ValueGetterGrid.js new file mode 100644 index 00000000000..31547aaf644 --- /dev/null +++ b/docs/data/data-grid/column-definition/ValueGetterGrid.js @@ -0,0 +1,33 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +function getFullName(params) { + return `${params.row.firstName || ''} ${params.row.lastName || ''}`; +} + +const columns = [ + { field: 'firstName', headerName: 'First name', width: 130 }, + { field: 'lastName', headerName: 'Last name', width: 130 }, + { + field: 'fullName', + headerName: 'Full name', + width: 160, + valueGetter: getFullName, + }, +]; + +const rows = [ + { id: 1, lastName: 'Snow', firstName: 'Jon' }, + { id: 2, lastName: 'Lannister', firstName: 'Cersei' }, + { id: 3, lastName: 'Lannister', firstName: 'Jaime' }, + { id: 4, lastName: 'Stark', firstName: 'Arya' }, + { id: 5, lastName: 'Targaryen', firstName: 'Daenerys' }, +]; + +export default function ValueGetterGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-definition/ValueGetterGrid.tsx b/docs/data/data-grid/column-definition/ValueGetterGrid.tsx new file mode 100644 index 00000000000..b161ba7b031 --- /dev/null +++ b/docs/data/data-grid/column-definition/ValueGetterGrid.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid'; + +function getFullName(params: GridValueGetterParams) { + return `${params.row.firstName || ''} ${params.row.lastName || ''}`; +} + +const columns: GridColDef[] = [ + { field: 'firstName', headerName: 'First name', width: 130 }, + { field: 'lastName', headerName: 'Last name', width: 130 }, + { + field: 'fullName', + headerName: 'Full name', + width: 160, + valueGetter: getFullName, + }, +]; + +const rows = [ + { id: 1, lastName: 'Snow', firstName: 'Jon' }, + { id: 2, lastName: 'Lannister', firstName: 'Cersei' }, + { id: 3, lastName: 'Lannister', firstName: 'Jaime' }, + { id: 4, lastName: 'Stark', firstName: 'Arya' }, + { id: 5, lastName: 'Targaryen', firstName: 'Daenerys' }, +]; + +export default function ValueGetterGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-definition/ValueGetterGrid.tsx.preview b/docs/data/data-grid/column-definition/ValueGetterGrid.tsx.preview new file mode 100644 index 00000000000..074afd47c44 --- /dev/null +++ b/docs/data/data-grid/column-definition/ValueGetterGrid.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-definition/VisibleColumnsSelectorsNoSnap.js b/docs/data/data-grid/column-definition/VisibleColumnsSelectorsNoSnap.js new file mode 100644 index 00000000000..79fa43e3716 --- /dev/null +++ b/docs/data/data-grid/column-definition/VisibleColumnsSelectorsNoSnap.js @@ -0,0 +1,6 @@ +import React from 'react'; +import SelectorsDocs from 'docsx/src/modules/components/SelectorsDocs'; + +export default function VisibleColumnsSelectorsNoSnap() { + return ; +} diff --git a/docs/data/data-grid/column-definition/column-definition.md b/docs/data/data-grid/column-definition/column-definition.md new file mode 100644 index 00000000000..810d08519a6 --- /dev/null +++ b/docs/data/data-grid/column-definition/column-definition.md @@ -0,0 +1,245 @@ +--- +title: Data Grid - Column definition +--- + +# Data Grid - Column definition + +

Define your columns.

+ +The columns are defined with the `columns` prop which has the type `GridColDef[]`. + +`field` is the only required property since it's the column identifier. It's also used to match with `GridRowModel` values. + +```ts +interface GridColDef { + /** + * The column identifier. It's used to match with [[GridRowModel]] values. + */ + field: string; + … +} +``` + +{{"demo": "BasicColumnsGrid.js", "bg": "inline"}} + +:::warning +The `columns` prop should keep the same reference between two renders. +The columns are designed to be definitions, to never change once the component is mounted. +Otherwise, you take the risk of losing elements like column width or order. +You can create the array outside the render function or memoize it. +::: + +## Providing content + +By default, the grid uses the field of a column to get its value. +For instance, the column with field `name` will render the value stored in `row.name`. +But for some columns, it can be useful to manually get and format the value to render. + +### Value getter + +Sometimes a column might not have a corresponding value, or you might want to render a combination of different fields. + +To achieve that, set the `valueGetter` attribute of `GridColDef` as in the example below. + +```tsx +function getFullName(params) { + return `${params.row.firstName || ''} ${params.row.lastName || ''}`; +} + +const columns: GridColDef[] = [ + { field: 'firstName', headerName: 'First name', width: 130 }, + { field: 'lastName', headerName: 'Last name', width: 130 }, + { + field: 'fullName', + headerName: 'Full name', + width: 160, + valueGetter: getFullName, + }, +]; +``` + +{{"demo": "ValueGetterGrid.js", "bg": "inline"}} + +The value generated is used for filtering, sorting, rendering, etc. unless overridden by a more specific configuration. + +### Value formatter + +The value formatter allows you to convert the value before displaying it. +Common use cases include converting a JavaScript `Date` object to a date string or a `Number` into a formatted number (e.g. "1,000.50"). + +In the following demo, a formatter is used to display the tax rate's decimal value (e.g. 0.2) as a percentage (e.g. 20%). + +{{"demo": "ValueFormatterGrid.js", "bg": "inline"}} + +The value generated is only used for rendering purposes. +Filtering and sorting do not rely on the formatted value. +Use the [`valueParser`](/x/react-data-grid/cells/#value-parser) to support filtering. + +## Rendering cells + +By default, the grid renders the value as a string in the cell. +It resolves the rendered output in the following order: + +1. `renderCell() => ReactElement` +2. `valueFormatter() => string` +3. `valueGetter() => string` +4. `row[field]` + +The `renderCell` method of the column definitions is similar to `valueFormatter`. +However, it trades to be able to only render in a cell in exchange for allowing to return a React node (instead of a string). + +```tsx +const columns: GridColDef[] = [ + { + field: 'date', + headerName: 'Year', + renderCell: (params: GridRenderCellParams) => ( + + {params.value.getFullYear()} + + + ), + }, +]; +``` + +{{"demo": "RenderCellGrid.js", "bg": "inline", "defaultCodeOpen": false }} + +:::warning +Using `renderCell`, requires paying attention to the following points. +If the type of the value returned by `valueGetter` does not correspond to the column's `type`, you should: + +- handle [sorting](/x/react-data-grid/sorting/#custom-comparator) by providing `sortComparator` to the column. +- set a `valueFormatter` providing a representation for the value to be used when [exporting](/x/react-data-grid/export/#exported-cells) the data. + +::: + +### Styling cells + +You can check the [styling cells](/x/react-data-grid/style/#styling-cells) section for more information. + +### Making accessible cells + +Cell content should not be in the tab sequence except if cell is focused. +You can check the [tab sequence](/x/react-data-grid/accessibility/#tab-sequence) section for more information. + +### Expand cell renderer + +By default, the grid cuts the content of a cell and renders an ellipsis if the content of the cell does not fit in the cell. +As a workaround, you can create a cell renderer that will allow seeing the full content of the cell in the grid. + +{{"demo": "RenderExpandCellGrid.js", "bg": "inline"}} + +## Column types + +To facilitate the configuration of the columns, some column types are predefined. +By default, columns are assumed to hold strings, so the default column string type will be applied. As a result, column sorting will use the string comparator, and the column content will be aligned to the left side of the cell. + +The following are the native column types: + +- `'string'` (default) +- `'number'` +- `'date'` +- `'dateTime'` +- `'boolean'` +- `'singleSelect'` +- `'actions'` + +### Converting types + +Default methods, such as filtering and sorting, assume that the type of the values will match the type of the column specified in `type`. +For example, values of column with `type: 'dateTime'` are expecting to be stored as a `Date()` objects. +If for any reason, your data type is not the correct one, you can use `valueGetter` to parse the value to the correct type. + +```tsx +{ + field: 'lastLogin', + type: 'dateTime', + valueGetter: ({ value }) => value && new Date(value), +} +``` + +### Special properties + +To use most of the column types, you only need to define the `type` property in your column definition. +However, some types require additional properties to be set to make them work correctly: + +- If the column type is `'singleSelect'`, you also need to set the `valueOptions` property in the respective column definition. These values are options used for filtering and editing. + + ```tsx + { + field: 'country', + type: 'singleSelect', + valueOptions: ['United Kingdom', 'Spain', 'Brazil'] + } + ``` + + :::warning + When using objects values for `valueOptions` you need to provide `value` and `label` fields for each option: `{ value: string, label: string }` + ::: + +- If the column type is `'actions'`, you need to provide a `getActions` function that returns an array of actions available for each row (React elements). + You can add the `showInMenu` prop on the returned React elements to signal the data grid to group these actions inside a row menu. + + ```tsx + { + field: 'actions', + type: 'actions', + getActions: (params: GridRowParams) => [ + , + , + ] + } + ``` + +{{"demo": "ColumnTypesGrid.js", "bg": "inline"}} + +## Custom column types + +You can extend the native column types with your own by simply spreading the necessary properties. + +The demo below defines a new column type: `usdPrice` that extends the native `number` column type. + +```ts +const usdPrice: GridColTypeDef = { + type: 'number', + width: 130, + valueFormatter: ({ value }) => valueFormatter.format(Number(value)), + cellClassName: 'font-tabular-nums', +}; +``` + +{{"demo": "CustomColumnTypesGrid.js", "bg": "inline"}} + +:::info +If an unsupported column type is used, the `string` column type will be used instead. +::: + +## Selectors [](https://mui.com/store/items/mui-x-pro/) + +### Visible columns + +Those selectors do not take into account hidden columns. + +{{"demo": "VisibleColumnsSelectorsNoSnap.js", "bg": "inline", "hideToolbar": true}} + +### Defined columns + +Those selectors consider all the defined columns, including hidden ones. + +{{"demo": "ColumnsSelectorsNoSnap.js", "bg": "inline", "hideToolbar": true}} + +More information about the selectors and how to use them on the [dedicated page](/x/react-data-grid/state/#access-the-state). + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/column-dimensions/ColumnFluidWidthGrid.js b/docs/data/data-grid/column-dimensions/ColumnFluidWidthGrid.js new file mode 100644 index 00000000000..1abc10db9a2 --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnFluidWidthGrid.js @@ -0,0 +1,36 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 20, + }, +]; + +export default function ColumnFluidWidthGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-dimensions/ColumnFluidWidthGrid.tsx b/docs/data/data-grid/column-dimensions/ColumnFluidWidthGrid.tsx new file mode 100644 index 00000000000..1abc10db9a2 --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnFluidWidthGrid.tsx @@ -0,0 +1,36 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 20, + }, +]; + +export default function ColumnFluidWidthGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-dimensions/ColumnMinWidthGrid.js b/docs/data/data-grid/column-dimensions/ColumnMinWidthGrid.js new file mode 100644 index 00000000000..6e940b98054 --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnMinWidthGrid.js @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 38, + }, +]; + +export default function ColumnMinWidthGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-dimensions/ColumnMinWidthGrid.tsx b/docs/data/data-grid/column-dimensions/ColumnMinWidthGrid.tsx new file mode 100644 index 00000000000..6e940b98054 --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnMinWidthGrid.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 38, + }, +]; + +export default function ColumnMinWidthGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-dimensions/ColumnMinWidthGrid.tsx.preview b/docs/data/data-grid/column-dimensions/ColumnMinWidthGrid.tsx.preview new file mode 100644 index 00000000000..361ae65ed91 --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnMinWidthGrid.tsx.preview @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-dimensions/ColumnSizingGrid.js b/docs/data/data-grid/column-dimensions/ColumnSizingGrid.js new file mode 100644 index 00000000000..7fd26eda676 --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnSizingGrid.js @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 20, + }, +]; + +export default function ColumnSizingGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-dimensions/ColumnSizingGrid.tsx b/docs/data/data-grid/column-dimensions/ColumnSizingGrid.tsx new file mode 100644 index 00000000000..7fd26eda676 --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnSizingGrid.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 20, + }, +]; + +export default function ColumnSizingGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-dimensions/ColumnSizingGrid.tsx.preview b/docs/data/data-grid/column-dimensions/ColumnSizingGrid.tsx.preview new file mode 100644 index 00000000000..2beedd9df6d --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnSizingGrid.tsx.preview @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-dimensions/ColumnWidthGrid.js b/docs/data/data-grid/column-dimensions/ColumnWidthGrid.js new file mode 100644 index 00000000000..0c93738a97e --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnWidthGrid.js @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 38, + }, +]; + +export default function ColumnWidthGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-dimensions/ColumnWidthGrid.tsx b/docs/data/data-grid/column-dimensions/ColumnWidthGrid.tsx new file mode 100644 index 00000000000..0c93738a97e --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnWidthGrid.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 38, + }, +]; + +export default function ColumnWidthGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-dimensions/ColumnWidthGrid.tsx.preview b/docs/data/data-grid/column-dimensions/ColumnWidthGrid.tsx.preview new file mode 100644 index 00000000000..de41cd9bb8e --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnWidthGrid.tsx.preview @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-dimensions/column-dimensions.md b/docs/data/data-grid/column-dimensions/column-dimensions.md new file mode 100644 index 00000000000..108c5889e94 --- /dev/null +++ b/docs/data/data-grid/column-dimensions/column-dimensions.md @@ -0,0 +1,68 @@ +--- +title: Data Grid - Column dimensions +--- + +# Data Grid - Column dimensions + +

Customize the dimensions and resizing behavior of your columns.

+ +## Column width + +By default, the columns have a width of 100px. +This is an arbitrary, easy-to-remember value. +To change the width of a column, use the `width` property available in `GridColDef`. + +{{"demo": "ColumnWidthGrid.js", "bg": "inline"}} + +### Minimum width + +By default, the columns have a minimum width of 50px. +This is an arbitrary, easy-to-remember value. +To change the minimum width of a column, use the `minWidth` property available in `GridColDef`. + +{{"demo": "ColumnMinWidthGrid.js", "bg": "inline"}} + +### Fluid width + +Column fluidity or responsiveness can be achieved by setting the `flex` property in `GridColDef`. + +The `flex` property accepts a value between 0 and ∞. +It works by dividing the remaining space in the grid among all flex columns in proportion to their `flex` value. + +For example, consider a grid with a total width of 500px that has three columns: the first with `width: 200`; the second with `flex: 1`; and the third with `flex: 0.5`. +The first column will be 200px wide, leaving 300px remaining. The column with `flex: 1` is twice the size of `flex: 0.5`, which means that final sizes will be: 200px, 200px, 100px. + +To set a minimum and maximum width for a `flex` column set the `minWidth` and the `maxWidth` property in `GridColDef`. + +:::info +Before using fluid width, note that: + +- `flex` doesn't work together with `width`. If you set both `flex` and `width` in `GridColDef`, `flex` will override `width`. +- `flex` doesn't work if the combined width of the columns that have `width` is more than the width of the grid itself. If that is the case a scroll bar will be visible, and the columns that have `flex` will default back to their base value of 100px. + +::: + +{{"demo": "ColumnFluidWidthGrid.js", "bg": "inline"}} + +## Resizing [](https://mui.com/store/items/mui-x-pro/) + +By default, `DataGridPro` allows all columns to be resized by dragging the right portion of the column separator. + +To prevent the resizing of a column, set `resizable: false` in the `GridColDef`. +Alternatively, to disable all columns resize, set the prop `disableColumnResize={true}`. + +To restrict resizing a column under a certain width set the `minWidth` property in `GridColDef`. + +To restrict resizing a column above a certain width set the `maxWidth` property in `GridColDef`. + +{{"demo": "ColumnSizingGrid.js", "disableAd": true, "bg": "inline"}} + +To capture changes in the width of a column there are two callbacks that are called: + +- `onColumnResize`: Called while a column is being resized. +- `onColumnWidthChange`: Called after the width of a column is changed, but not during resizing. + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/column-groups/column-groups.md b/docs/data/data-grid/column-groups/column-groups.md new file mode 100644 index 00000000000..27c0344a0c4 --- /dev/null +++ b/docs/data/data-grid/column-groups/column-groups.md @@ -0,0 +1,20 @@ +--- +title: Data Grid - Column groups 🚧 +--- + +# Data Grid - Column groups 🚧 + +

Group your columns.

+ +:::warning +This feature isn't implemented yet. It's coming. + +👍 Upvote [issue #195](https://github.com/mui/mui-x/issues/195) if you want to see it land faster. +::: + +Grouping columns allows you to have multiple levels of columns in your header and the ability, if needed, to 'open and close' column groups to show and hide additional columns. + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/column-header/ColumnMenuGrid.js b/docs/data/data-grid/column-header/ColumnMenuGrid.js new file mode 100644 index 00000000000..ed82e19e0b4 --- /dev/null +++ b/docs/data/data-grid/column-header/ColumnMenuGrid.js @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function ColumnMenuGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 20, + maxColumns: 5, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-header/ColumnMenuGrid.tsx b/docs/data/data-grid/column-header/ColumnMenuGrid.tsx new file mode 100644 index 00000000000..ed82e19e0b4 --- /dev/null +++ b/docs/data/data-grid/column-header/ColumnMenuGrid.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function ColumnMenuGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 20, + maxColumns: 5, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-header/ColumnMenuGrid.tsx.preview b/docs/data/data-grid/column-header/ColumnMenuGrid.tsx.preview new file mode 100644 index 00000000000..da05302dbe7 --- /dev/null +++ b/docs/data/data-grid/column-header/ColumnMenuGrid.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-header/HeaderColumnsGrid.js b/docs/data/data-grid/column-header/HeaderColumnsGrid.js new file mode 100644 index 00000000000..36fec7b4ac2 --- /dev/null +++ b/docs/data/data-grid/column-header/HeaderColumnsGrid.js @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 20, + }, +]; + +export default function HeaderColumnsGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-header/HeaderColumnsGrid.tsx b/docs/data/data-grid/column-header/HeaderColumnsGrid.tsx new file mode 100644 index 00000000000..36fec7b4ac2 --- /dev/null +++ b/docs/data/data-grid/column-header/HeaderColumnsGrid.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 20, + }, +]; + +export default function HeaderColumnsGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-header/HeaderColumnsGrid.tsx.preview b/docs/data/data-grid/column-header/HeaderColumnsGrid.tsx.preview new file mode 100644 index 00000000000..4a502e0f6fa --- /dev/null +++ b/docs/data/data-grid/column-header/HeaderColumnsGrid.tsx.preview @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-header/RenderHeaderGrid.js b/docs/data/data-grid/column-header/RenderHeaderGrid.js new file mode 100644 index 00000000000..1827cda36a5 --- /dev/null +++ b/docs/data/data-grid/column-header/RenderHeaderGrid.js @@ -0,0 +1,41 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +const columns = [ + { + field: 'date', + width: 150, + type: 'date', + renderHeader: () => ( + + {'Birthday '} + + 🎂 + + + ), + }, +]; + +const rows = [ + { + id: 1, + date: new Date(1979, 0, 1), + }, + { + id: 2, + date: new Date(1984, 1, 1), + }, + { + id: 3, + date: new Date(1992, 2, 1), + }, +]; + +export default function RenderHeaderGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-header/RenderHeaderGrid.tsx b/docs/data/data-grid/column-header/RenderHeaderGrid.tsx new file mode 100644 index 00000000000..4eb3e7fc26e --- /dev/null +++ b/docs/data/data-grid/column-header/RenderHeaderGrid.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import { DataGrid, GridColDef } from '@mui/x-data-grid'; + +const columns: GridColDef[] = [ + { + field: 'date', + width: 150, + type: 'date', + renderHeader: () => ( + + {'Birthday '} + + 🎂 + + + ), + }, +]; + +const rows = [ + { + id: 1, + date: new Date(1979, 0, 1), + }, + { + id: 2, + date: new Date(1984, 1, 1), + }, + { + id: 3, + date: new Date(1992, 2, 1), + }, +]; + +export default function RenderHeaderGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-header/RenderHeaderGrid.tsx.preview b/docs/data/data-grid/column-header/RenderHeaderGrid.tsx.preview new file mode 100644 index 00000000000..074afd47c44 --- /dev/null +++ b/docs/data/data-grid/column-header/RenderHeaderGrid.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-header/column-header.md b/docs/data/data-grid/column-header/column-header.md new file mode 100644 index 00000000000..753e19376fe --- /dev/null +++ b/docs/data/data-grid/column-header/column-header.md @@ -0,0 +1,54 @@ +--- +title: Data Grid - Column header +--- + +# Data Grid - Column header + +

Customize your columns header.

+ +You can configure the headers with: + +- `headerName`: The title of the column rendered in the column header cell. +- `description`: The description of the column rendered as tooltip if the column header name is not fully displayed. + +{{"demo": "HeaderColumnsGrid.js", "bg": "inline"}} + +## Custom header renderer + +You can customize the look of each header with the `renderHeader` method. +It takes precedence over the `headerName` property. + +```tsx +const columns: GridColDef[] = [ + { + field: 'date', + width: 150, + type: 'date', + renderHeader: (params: GridColumnHeaderParams) => ( + + {'Birthday '} + + 🎂 + + + ), + }, +]; +``` + +{{"demo": "RenderHeaderGrid.js", "bg": "inline"}} + +## Styling header + +You can check the [styling header](/x/react-data-grid/style/#styling-column-headers) section for more information. + +## Column menu + +By default, each column header displays a column menu. The column menu allows actions to be performed in the context of the target column, e.g. filtering. To disable the column menu, set the prop `disableColumnMenu={true}`. + +{{"demo": "ColumnMenuGrid.js", "bg": "inline"}} + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/column-ordering/ColumnOrderingDisabledGrid.js b/docs/data/data-grid/column-ordering/ColumnOrderingDisabledGrid.js new file mode 100644 index 00000000000..e88d135a71d --- /dev/null +++ b/docs/data/data-grid/column-ordering/ColumnOrderingDisabledGrid.js @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 20, + }, +]; + +export default function ColumnOrderingDisabledGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-ordering/ColumnOrderingDisabledGrid.tsx b/docs/data/data-grid/column-ordering/ColumnOrderingDisabledGrid.tsx new file mode 100644 index 00000000000..e88d135a71d --- /dev/null +++ b/docs/data/data-grid/column-ordering/ColumnOrderingDisabledGrid.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 20, + }, +]; + +export default function ColumnOrderingDisabledGrid() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-ordering/ColumnOrderingDisabledGrid.tsx.preview b/docs/data/data-grid/column-ordering/ColumnOrderingDisabledGrid.tsx.preview new file mode 100644 index 00000000000..e57b5d1ad06 --- /dev/null +++ b/docs/data/data-grid/column-ordering/ColumnOrderingDisabledGrid.tsx.preview @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-ordering/ColumnOrderingGrid.js b/docs/data/data-grid/column-ordering/ColumnOrderingGrid.js new file mode 100644 index 00000000000..a44ca17312c --- /dev/null +++ b/docs/data/data-grid/column-ordering/ColumnOrderingGrid.js @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function ColumnOrderingGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 20, + maxColumns: 20, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-ordering/ColumnOrderingGrid.tsx b/docs/data/data-grid/column-ordering/ColumnOrderingGrid.tsx new file mode 100644 index 00000000000..a44ca17312c --- /dev/null +++ b/docs/data/data-grid/column-ordering/ColumnOrderingGrid.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function ColumnOrderingGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 20, + maxColumns: 20, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-ordering/ColumnOrderingGrid.tsx.preview b/docs/data/data-grid/column-ordering/ColumnOrderingGrid.tsx.preview new file mode 100644 index 00000000000..0f1aa8c3315 --- /dev/null +++ b/docs/data/data-grid/column-ordering/ColumnOrderingGrid.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-ordering/column-ordering.md b/docs/data/data-grid/column-ordering/column-ordering.md new file mode 100644 index 00000000000..a7ad2841874 --- /dev/null +++ b/docs/data/data-grid/column-ordering/column-ordering.md @@ -0,0 +1,31 @@ +--- +title: Data Grid - Column ordering +--- + +# Data Grid - Column ordering [](https://mui.com/store/items/mui-x-pro/) + +

Drag and drop your columns to reorder them.

+ +By default, columns are ordered according to the order they are included in the `columns` array. + +By default, `DataGridPro` allows all column reordering by dragging the header cells and moving them left or right. + +{{"demo": "ColumnOrderingGrid.js", "disableAd": true, "bg": "inline"}} + +To disable reordering on all columns, set the prop `disableColumnReorder={true}`. + +To disable reordering in a specific column, set the `disableReorder` property to true in the `GridColDef` of the respective column. + +{{"demo": "ColumnOrderingDisabledGrid.js", "disableAd": true, "bg": "inline"}} + +In addition, column reordering emits the following events that can be imported: + +- `columnHeaderDragStart`: emitted when dragging of a header cell starts. +- `columnHeaderDragEnter`: emitted when the cursor enters another header cell while dragging. +- `columnHeaderDragOver`: emitted when dragging a header cell over another header cell. +- `columnHeaderDragEnd`: emitted when dragging of a header cell stops. + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/column-pinning/BasicColumnPinning.js b/docs/data/data-grid/column-pinning/BasicColumnPinning.js new file mode 100644 index 00000000000..300a9a832a6 --- /dev/null +++ b/docs/data/data-grid/column-pinning/BasicColumnPinning.js @@ -0,0 +1,134 @@ +import * as React from 'react'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditIcon from '@mui/icons-material/Edit'; +import { DataGridPro, GridActionsCellItem } from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomEmail, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function BasicColumnPinning() { + return ( +
+ +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 160, editable: true }, + { field: 'email', headerName: 'Email', width: 200, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, + { + field: 'actions', + type: 'actions', + width: 100, + getActions: () => [ + } label="Edit" />, + } label="Delete" />, + ], + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + email: randomEmail(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + email: randomEmail(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + email: randomEmail(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + email: randomEmail(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + email: randomEmail(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 6, + name: randomTraderName(), + email: randomEmail(), + age: 27, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 7, + name: randomTraderName(), + email: randomEmail(), + age: 18, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 8, + name: randomTraderName(), + email: randomEmail(), + age: 31, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 9, + name: randomTraderName(), + email: randomEmail(), + age: 24, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 10, + name: randomTraderName(), + email: randomEmail(), + age: 35, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/column-pinning/BasicColumnPinning.tsx b/docs/data/data-grid/column-pinning/BasicColumnPinning.tsx new file mode 100644 index 00000000000..37979e125ab --- /dev/null +++ b/docs/data/data-grid/column-pinning/BasicColumnPinning.tsx @@ -0,0 +1,139 @@ +import * as React from 'react'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditIcon from '@mui/icons-material/Edit'; +import { + DataGridPro, + GridColumns, + GridRowsProp, + GridActionsCellItem, +} from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomEmail, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function BasicColumnPinning() { + return ( +
+ +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 160, editable: true }, + { field: 'email', headerName: 'Email', width: 200, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, + { + field: 'actions', + type: 'actions', + width: 100, + getActions: () => [ + } label="Edit" />, + } label="Delete" />, + ], + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + email: randomEmail(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + email: randomEmail(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + email: randomEmail(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + email: randomEmail(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + email: randomEmail(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 6, + name: randomTraderName(), + email: randomEmail(), + age: 27, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 7, + name: randomTraderName(), + email: randomEmail(), + age: 18, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 8, + name: randomTraderName(), + email: randomEmail(), + age: 31, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 9, + name: randomTraderName(), + email: randomEmail(), + age: 24, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 10, + name: randomTraderName(), + email: randomEmail(), + age: 35, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/column-pinning/BasicColumnPinning.tsx.preview b/docs/data/data-grid/column-pinning/BasicColumnPinning.tsx.preview new file mode 100644 index 00000000000..b3a5f5e1524 --- /dev/null +++ b/docs/data/data-grid/column-pinning/BasicColumnPinning.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-pinning/ColumnPinningApiNoSnap.js b/docs/data/data-grid/column-pinning/ColumnPinningApiNoSnap.js new file mode 100644 index 00000000000..e2f5d78dc53 --- /dev/null +++ b/docs/data/data-grid/column-pinning/ColumnPinningApiNoSnap.js @@ -0,0 +1,7 @@ +import React from 'react'; +import ApiDocs from 'docsx/src/modules/components/ApiDocs'; +import api from 'docsx/pages/x/api/data-grid/grid-column-pinning-api.json'; + +export default function ColumnPinningApiNoSnap() { + return ; +} diff --git a/docs/data/data-grid/column-pinning/ColumnPinningWithCheckboxSelection.js b/docs/data/data-grid/column-pinning/ColumnPinningWithCheckboxSelection.js new file mode 100644 index 00000000000..4ac5c90aa9a --- /dev/null +++ b/docs/data/data-grid/column-pinning/ColumnPinningWithCheckboxSelection.js @@ -0,0 +1,104 @@ +import * as React from 'react'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditIcon from '@mui/icons-material/Edit'; +import { + DataGridPro, + GridActionsCellItem, + GRID_CHECKBOX_SELECTION_COL_DEF, +} from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomEmail, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function ColumnPinningWithCheckboxSelection() { + return ( +
+ +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 160, editable: true }, + { field: 'email', headerName: 'Email', width: 200, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, + { + field: 'actions', + type: 'actions', + width: 100, + getActions: () => [ + } label="Edit" />, + } label="Delete" />, + ], + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + email: randomEmail(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + email: randomEmail(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + email: randomEmail(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + email: randomEmail(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + email: randomEmail(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/column-pinning/ColumnPinningWithCheckboxSelection.tsx b/docs/data/data-grid/column-pinning/ColumnPinningWithCheckboxSelection.tsx new file mode 100644 index 00000000000..576f4bf42a6 --- /dev/null +++ b/docs/data/data-grid/column-pinning/ColumnPinningWithCheckboxSelection.tsx @@ -0,0 +1,106 @@ +import * as React from 'react'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditIcon from '@mui/icons-material/Edit'; +import { + DataGridPro, + GridColumns, + GridRowsProp, + GridActionsCellItem, + GRID_CHECKBOX_SELECTION_COL_DEF, +} from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomEmail, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function ColumnPinningWithCheckboxSelection() { + return ( +
+ +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 160, editable: true }, + { field: 'email', headerName: 'Email', width: 200, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, + { + field: 'actions', + type: 'actions', + width: 100, + getActions: () => [ + } label="Edit" />, + } label="Delete" />, + ], + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + email: randomEmail(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + email: randomEmail(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + email: randomEmail(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + email: randomEmail(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + email: randomEmail(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/column-pinning/ColumnPinningWithCheckboxSelection.tsx.preview b/docs/data/data-grid/column-pinning/ColumnPinningWithCheckboxSelection.tsx.preview new file mode 100644 index 00000000000..6174dddbc38 --- /dev/null +++ b/docs/data/data-grid/column-pinning/ColumnPinningWithCheckboxSelection.tsx.preview @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-pinning/ControlPinnedColumns.js b/docs/data/data-grid/column-pinning/ControlPinnedColumns.js new file mode 100644 index 00000000000..e76f209395b --- /dev/null +++ b/docs/data/data-grid/column-pinning/ControlPinnedColumns.js @@ -0,0 +1,98 @@ +import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomEmail, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; +import Alert from '@mui/material/Alert'; + +export default function ControlPinnedColumns() { + const [pinnedColumns, setPinnedColumns] = React.useState({ + left: ['name'], + }); + + const handlePinnedColumnsChange = React.useCallback((updatedPinnedColumns) => { + setPinnedColumns(updatedPinnedColumns); + }, []); + + return ( +
+ + pinnedColumns: {JSON.stringify(pinnedColumns)} + +
+ +
+
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'email', headerName: 'Email', width: 200, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + email: randomEmail(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + email: randomEmail(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + email: randomEmail(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + email: randomEmail(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + email: randomEmail(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/column-pinning/ControlPinnedColumns.tsx b/docs/data/data-grid/column-pinning/ControlPinnedColumns.tsx new file mode 100644 index 00000000000..04a2aadfd9f --- /dev/null +++ b/docs/data/data-grid/column-pinning/ControlPinnedColumns.tsx @@ -0,0 +1,106 @@ +import * as React from 'react'; +import { + DataGridPro, + GridColumns, + GridPinnedColumns, + GridRowsProp, +} from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomEmail, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; +import Alert from '@mui/material/Alert'; + +export default function ControlPinnedColumns() { + const [pinnedColumns, setPinnedColumns] = React.useState({ + left: ['name'], + }); + + const handlePinnedColumnsChange = React.useCallback( + (updatedPinnedColumns: GridPinnedColumns) => { + setPinnedColumns(updatedPinnedColumns); + }, + [], + ); + + return ( +
+ + pinnedColumns: {JSON.stringify(pinnedColumns)} + +
+ +
+
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'email', headerName: 'Email', width: 200, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + email: randomEmail(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + email: randomEmail(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + email: randomEmail(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + email: randomEmail(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + email: randomEmail(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/column-pinning/ControlPinnedColumns.tsx.preview b/docs/data/data-grid/column-pinning/ControlPinnedColumns.tsx.preview new file mode 100644 index 00000000000..deb69221419 --- /dev/null +++ b/docs/data/data-grid/column-pinning/ControlPinnedColumns.tsx.preview @@ -0,0 +1,11 @@ + + pinnedColumns: {JSON.stringify(pinnedColumns)} + +
+ +
\ No newline at end of file diff --git a/docs/data/data-grid/column-pinning/DisableColumnPinningButtons.js b/docs/data/data-grid/column-pinning/DisableColumnPinningButtons.js new file mode 100644 index 00000000000..0141adb23a4 --- /dev/null +++ b/docs/data/data-grid/column-pinning/DisableColumnPinningButtons.js @@ -0,0 +1,117 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { + DataGridPro, + GridColumnMenuContainer, + SortGridMenuItems, + HideGridColMenuItem, + GridColumnsMenuItem, + GridFilterMenuItem, +} from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomEmail, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +function CustomColumnMenu(props) { + const { hideMenu, currentColumn, color, ...other } = props; + + return ( + + + + + + + ); +} + +CustomColumnMenu.propTypes = { + color: PropTypes.string, + currentColumn: PropTypes.object.isRequired, + hideMenu: PropTypes.func.isRequired, +}; + +export { CustomColumnMenu }; + +export default function DisableColumnPinningButtons() { + return ( +
+ +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 160, editable: true }, + { field: 'email', headerName: 'Email', width: 200, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + email: randomEmail(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + email: randomEmail(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + email: randomEmail(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + email: randomEmail(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + email: randomEmail(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/column-pinning/DisableColumnPinningButtons.tsx b/docs/data/data-grid/column-pinning/DisableColumnPinningButtons.tsx new file mode 100644 index 00000000000..91c656fab06 --- /dev/null +++ b/docs/data/data-grid/column-pinning/DisableColumnPinningButtons.tsx @@ -0,0 +1,111 @@ +import * as React from 'react'; +import { + DataGridPro, + GridColumns, + GridRowsProp, + GridColumnMenuContainer, + SortGridMenuItems, + HideGridColMenuItem, + GridColumnsMenuItem, + GridFilterMenuItem, + GridColumnMenuProps, +} from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomEmail, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export function CustomColumnMenu(props: GridColumnMenuProps) { + const { hideMenu, currentColumn, color, ...other } = props; + + return ( + + + + + + + ); +} + +export default function DisableColumnPinningButtons() { + return ( +
+ +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 160, editable: true }, + { field: 'email', headerName: 'Email', width: 200, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + email: randomEmail(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + email: randomEmail(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + email: randomEmail(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + email: randomEmail(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + email: randomEmail(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/column-pinning/DisableColumnPinningButtons.tsx.preview b/docs/data/data-grid/column-pinning/DisableColumnPinningButtons.tsx.preview new file mode 100644 index 00000000000..7beb8ff8ae5 --- /dev/null +++ b/docs/data/data-grid/column-pinning/DisableColumnPinningButtons.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-pinning/column-pinning.md b/docs/data/data-grid/column-pinning/column-pinning.md new file mode 100644 index 00000000000..59ab0b59238 --- /dev/null +++ b/docs/data/data-grid/column-pinning/column-pinning.md @@ -0,0 +1,87 @@ +--- +title: Data Grid - Column pinning +--- + +# Data Grid - Column pinning [](https://mui.com/store/items/mui-x-pro/) + +

Pin columns to keep them visible at all time.

+ +Pinned (or frozen, locked, or sticky) columns are columns that are visible at all time while the user scrolls the grid horizontally. +They can be pinned either to the left or right side and cannot be reordered. + +To pin a column, there are a few ways: + +- Using the `initialState` prop +- [Controlling](#controlling-the-pinned-columns) the `pinnedColumns` and `onPinnedColumnsChange` props +- Dedicated buttons in the column menu +- Accessing the [imperative](#apiref) API + +To set pinned columns via `initialState`, pass an object with the following shape to this prop: + +```ts +interface GridPinnedColumns { + left?: string[]; // Optional field names to pin to the left + right?: string[]; // Optional field names to pin to the right +} +``` + +The following demos illustrates how this approach works: + +{{"demo": "BasicColumnPinning.js", "disableAd": true, "bg": "inline"}} + +:::info +The column pinning feature can be completely disabled with `disableColumnPinning`. + +```tsx + +``` + +::: + +:::warning +You may encounter issues if the sum of the widths of the pinned columns is larger than the width of the grid. +Make sure that the grid can accommodate properly, at least, these columns. +::: + +## Controlling the pinned columns + +While the `initialState` prop only works for setting pinned columns during the initialization, the `pinnedColumns` prop allows you to modify which columns are pinned at any time. +The value passed to it follows the same shape from the previous approach. +Use it together with `onPinnedColumnsChange` to know when a column is pinned or unpinned. + +{{"demo": "ControlPinnedColumns.js", "disableAd": true, "bg": "inline"}} + +## Blocking column unpinning + +It may be desirable to not allow a column to be unpinned. +The only thing required to achieve that is to hide the buttons added to the column menu. +This can be done in two ways: + +1. Per column, by setting `pinnable` to `false` in each `GridColDef`: + + ```tsx + // Default is `true`. + ``` + +2. By providing a custom menu, as demonstrated below: + +{{"demo": "DisableColumnPinningButtons.js", "disableAd": true, "bg": "inline"}} + +:::info +Using the `disableColumnMenu` prop also works, however, you disable completely the column menu with this approach. +::: + +## Pinning the checkbox selection column + +To pin the checkbox column added when using `checkboxSelection`, add `GRID_CHECKBOX_SELECTION_COL_DEF.field` to the list of pinned columns. + +{{"demo": "ColumnPinningWithCheckboxSelection.js", "disableAd": true, "bg": "inline"}} + +## apiRef + +{{"demo": "ColumnPinningApiNoSnap.js", "bg": "inline", "hideToolbar": true}} + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/column-spanning/ColumnSpanningDerived.js b/docs/data/data-grid/column-spanning/ColumnSpanningDerived.js new file mode 100644 index 00000000000..2ef04bd5ab4 --- /dev/null +++ b/docs/data/data-grid/column-spanning/ColumnSpanningDerived.js @@ -0,0 +1,185 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGridPro } from '@mui/x-data-grid-pro'; + +const slotTimesLookup = { + 0: '09:00 - 10:00', + 1: '10:00 - 11:00', + 2: '11:00 - 12:00', + 3: '12:00 - 13:00', + 4: '13:00 - 14:00', + 5: '14:00 - 15:00', + 6: '15:00 - 16:00', + 7: '16:00 - 17:00', +}; + +const rows = [ + { + id: 1, + day: 'Monday', + slots: ['Maths', 'English', 'English', 'Lab', '', 'Lab', 'Music', 'Music'], + }, + { + id: 2, + day: 'Tuesday', + slots: [ + 'Chemistry', + 'Chemistry', + 'Chemistry', + 'Physics', + '', + 'Maths', + 'Lab', + 'Dance', + ], + }, + { + id: 3, + day: 'Wednesday', + slots: ['Physics', 'English', 'Maths', 'Maths', '', 'Chemistry', 'Chemistry'], + }, + { + id: 4, + day: 'Thursday', + slots: [ + 'Music', + 'Music', + 'Chemistry', + 'Chemistry', + '', + 'Chemistry', + 'English', + 'English', + ], + }, + { + id: 5, + day: 'Friday', + slots: ['Maths', 'Dance', 'Dance', 'Physics', '', 'English'], + }, +]; + +const slotColumnCommonFields = { + sortable: false, + filterable: false, + pinnable: false, + hideable: false, + minWidth: 140, + cellClassName: (params) => params.value, + colSpan: ({ row, field, value }) => { + const index = Number(field); + let colSpan = 1; + for (let i = index + 1; i < row.slots.length; i += 1) { + const nextValue = row.slots[i]; + if (nextValue === value) { + colSpan += 1; + } else { + break; + } + } + return colSpan; + }, +}; + +const columns = [ + { + field: 'day', + headerName: 'Day', + }, + { + field: '0', + headerName: slotTimesLookup[0], + valueGetter: ({ row }) => row.slots[0], + ...slotColumnCommonFields, + }, + { + field: '1', + headerName: slotTimesLookup[1], + valueGetter: ({ row }) => row.slots[1], + ...slotColumnCommonFields, + }, + { + field: '2', + headerName: slotTimesLookup[2], + valueGetter: ({ row }) => row.slots[2], + ...slotColumnCommonFields, + }, + { + field: '3', + headerName: slotTimesLookup[3], + valueGetter: ({ row }) => row.slots[3], + ...slotColumnCommonFields, + }, + { + field: '4', + headerName: slotTimesLookup[4], + valueGetter: ({ row }) => row.slots[4], + ...slotColumnCommonFields, + }, + { + field: '5', + headerName: slotTimesLookup[5], + valueGetter: ({ row }) => row.slots[5], + ...slotColumnCommonFields, + }, + { + field: '6', + headerName: slotTimesLookup[6], + valueGetter: ({ row }) => row.slots[6], + ...slotColumnCommonFields, + }, + { + field: '7', + headerName: slotTimesLookup[7], + valueGetter: ({ row }) => row.slots[7], + ...slotColumnCommonFields, + }, +]; + +const rootStyles = { + width: '100%', + '& .Maths': { + backgroundColor: 'rgba(157, 255, 118, 0.49)', + }, + '& .English': { + backgroundColor: 'rgba(255, 255, 10, 0.49)', + }, + '& .Lab': { + backgroundColor: 'rgba(150, 150, 150, 0.49)', + }, + '& .Chemistry': { + backgroundColor: 'rgba(255, 150, 150, 0.49)', + }, + '& .Physics': { + backgroundColor: 'rgba(10, 150, 255, 0.49)', + }, + '& .Music': { + backgroundColor: 'rgba(224, 183, 60, 0.55)', + }, + '& .Dance': { + backgroundColor: 'rgba(200, 150, 255, 0.49)', + }, +}; + +export default function ColumnSpanningDerived() { + return ( + + + + ); +} diff --git a/docs/data/data-grid/column-spanning/ColumnSpanningDerived.tsx b/docs/data/data-grid/column-spanning/ColumnSpanningDerived.tsx new file mode 100644 index 00000000000..8273b733195 --- /dev/null +++ b/docs/data/data-grid/column-spanning/ColumnSpanningDerived.tsx @@ -0,0 +1,194 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGridPro, GridColDef, GridCellParams } from '@mui/x-data-grid-pro'; + +const slotTimesLookup = { + 0: '09:00 - 10:00', + 1: '10:00 - 11:00', + 2: '11:00 - 12:00', + 3: '12:00 - 13:00', + 4: '13:00 - 14:00', + 5: '14:00 - 15:00', + 6: '15:00 - 16:00', + 7: '16:00 - 17:00', +}; + +type Subject = + | 'Maths' + | 'English' + | 'Lab' + | 'Chemistry' + | 'Physics' + | 'Music' + | 'Dance'; + +const rows: Array<{ id: number; day: string; slots: Array }> = [ + { + id: 1, + day: 'Monday', + slots: ['Maths', 'English', 'English', 'Lab', '', 'Lab', 'Music', 'Music'], + }, + { + id: 2, + day: 'Tuesday', + slots: [ + 'Chemistry', + 'Chemistry', + 'Chemistry', + 'Physics', + '', + 'Maths', + 'Lab', + 'Dance', + ], + }, + { + id: 3, + day: 'Wednesday', + slots: ['Physics', 'English', 'Maths', 'Maths', '', 'Chemistry', 'Chemistry'], + }, + { + id: 4, + day: 'Thursday', + slots: [ + 'Music', + 'Music', + 'Chemistry', + 'Chemistry', + '', + 'Chemistry', + 'English', + 'English', + ], + }, + { + id: 5, + day: 'Friday', + slots: ['Maths', 'Dance', 'Dance', 'Physics', '', 'English'], + }, +]; + +const slotColumnCommonFields: Partial = { + sortable: false, + filterable: false, + pinnable: false, + hideable: false, + minWidth: 140, + cellClassName: (params: GridCellParams) => params.value, + colSpan: ({ row, field, value }: GridCellParams) => { + const index = Number(field); + let colSpan = 1; + for (let i = index + 1; i < row.slots.length; i += 1) { + const nextValue = row.slots[i]; + if (nextValue === value) { + colSpan += 1; + } else { + break; + } + } + return colSpan; + }, +}; + +const columns: GridColDef[] = [ + { + field: 'day', + headerName: 'Day', + }, + { + field: '0', + headerName: slotTimesLookup[0], + valueGetter: ({ row }) => row.slots[0], + ...slotColumnCommonFields, + }, + { + field: '1', + headerName: slotTimesLookup[1], + valueGetter: ({ row }) => row.slots[1], + ...slotColumnCommonFields, + }, + { + field: '2', + headerName: slotTimesLookup[2], + valueGetter: ({ row }) => row.slots[2], + ...slotColumnCommonFields, + }, + { + field: '3', + headerName: slotTimesLookup[3], + valueGetter: ({ row }) => row.slots[3], + ...slotColumnCommonFields, + }, + { + field: '4', + headerName: slotTimesLookup[4], + valueGetter: ({ row }) => row.slots[4], + ...slotColumnCommonFields, + }, + { + field: '5', + headerName: slotTimesLookup[5], + valueGetter: ({ row }) => row.slots[5], + ...slotColumnCommonFields, + }, + { + field: '6', + headerName: slotTimesLookup[6], + valueGetter: ({ row }) => row.slots[6], + ...slotColumnCommonFields, + }, + { + field: '7', + headerName: slotTimesLookup[7], + valueGetter: ({ row }) => row.slots[7], + ...slotColumnCommonFields, + }, +]; + +const rootStyles = { + width: '100%', + '& .Maths': { + backgroundColor: 'rgba(157, 255, 118, 0.49)', + }, + '& .English': { + backgroundColor: 'rgba(255, 255, 10, 0.49)', + }, + '& .Lab': { + backgroundColor: 'rgba(150, 150, 150, 0.49)', + }, + '& .Chemistry': { + backgroundColor: 'rgba(255, 150, 150, 0.49)', + }, + '& .Physics': { + backgroundColor: 'rgba(10, 150, 255, 0.49)', + }, + '& .Music': { + backgroundColor: 'rgba(224, 183, 60, 0.55)', + }, + '& .Dance': { + backgroundColor: 'rgba(200, 150, 255, 0.49)', + }, +}; + +export default function ColumnSpanningDerived() { + return ( + + + + ); +} diff --git a/docs/data/data-grid/column-spanning/ColumnSpanningDerived.tsx.preview b/docs/data/data-grid/column-spanning/ColumnSpanningDerived.tsx.preview new file mode 100644 index 00000000000..e49f54aef5c --- /dev/null +++ b/docs/data/data-grid/column-spanning/ColumnSpanningDerived.tsx.preview @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-spanning/ColumnSpanningFunction.js b/docs/data/data-grid/column-spanning/ColumnSpanningFunction.js new file mode 100644 index 00000000000..6faaf4ed263 --- /dev/null +++ b/docs/data/data-grid/column-spanning/ColumnSpanningFunction.js @@ -0,0 +1,118 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid } from '@mui/x-data-grid'; + +const items = [ + { id: 1, item: 'Paperclip', quantity: 100, price: 1.99 }, + { id: 2, item: 'Paper', quantity: 10, price: 30 }, + { id: 3, item: 'Pencil', quantity: 100, price: 1.25 }, +]; + +const rows = [ + ...items, + { id: 'SUBTOTAL', label: 'Subtotal', subtotal: 624 }, + { id: 'TAX', label: 'Tax', taxRate: 10, taxTotal: 62.4 }, + { id: 'TOTAL', label: 'Total', total: 686.4 }, +]; + +const baseColumnOptions = { + sortable: false, + pinnable: false, + hideable: false, +}; + +const columns = [ + { + field: 'item', + headerName: 'Item/Description', + ...baseColumnOptions, + flex: 3, + colSpan: ({ row }) => { + if (row.id === 'SUBTOTAL' || row.id === 'TOTAL') { + return 3; + } + if (row.id === 'TAX') { + return 2; + } + return undefined; + }, + valueGetter: ({ value, row }) => { + if (row.id === 'SUBTOTAL' || row.id === 'TAX' || row.id === 'TOTAL') { + return row.label; + } + return value; + }, + }, + { + field: 'quantity', + headerName: 'Quantity', + ...baseColumnOptions, + flex: 1, + sortable: false, + }, + { + field: 'price', + headerName: 'Price', + flex: 1, + ...baseColumnOptions, + valueGetter: ({ row, value }) => { + if (row.id === 'TAX') { + return `${row.taxRate}%`; + } + return value; + }, + }, + { + field: 'total', + headerName: 'Total', + flex: 1, + ...baseColumnOptions, + valueGetter: ({ row }) => { + if (row.id === 'SUBTOTAL') { + return row.subtotal; + } + if (row.id === 'TAX') { + return row.taxTotal; + } + if (row.id === 'TOTAL') { + return row.total; + } + return row.price * row.quantity; + }, + }, +]; + +const getCellClassName = ({ row, field }) => { + if (row.id === 'SUBTOTAL' || row.id === 'TOTAL' || row.id === 'TAX') { + if (field === 'item') { + return 'bold'; + } + } + return ''; +}; + +export default function ColumnSpanningFunction() { + return ( + + + + ); +} diff --git a/docs/data/data-grid/column-spanning/ColumnSpanningFunction.tsx b/docs/data/data-grid/column-spanning/ColumnSpanningFunction.tsx new file mode 100644 index 00000000000..677a9190863 --- /dev/null +++ b/docs/data/data-grid/column-spanning/ColumnSpanningFunction.tsx @@ -0,0 +1,141 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid, DataGridProps, GridColDef } from '@mui/x-data-grid'; + +const items = [ + { id: 1, item: 'Paperclip', quantity: 100, price: 1.99 }, + { id: 2, item: 'Paper', quantity: 10, price: 30 }, + { id: 3, item: 'Pencil', quantity: 100, price: 1.25 }, +]; + +type Item = typeof items[number]; + +interface SubtotalHeader { + id: 'SUBTOTAL'; + label: string; + subtotal: number; +} + +interface TaxHeader { + id: 'TAX'; + label: string; + taxRate: number; + taxTotal: number; +} + +interface TotalHeader { + id: 'TOTAL'; + label: string; + total: number; +} + +type Row = Item | SubtotalHeader | TaxHeader | TotalHeader; + +const rows: Row[] = [ + ...items, + { id: 'SUBTOTAL', label: 'Subtotal', subtotal: 624 }, + { id: 'TAX', label: 'Tax', taxRate: 10, taxTotal: 62.4 }, + { id: 'TOTAL', label: 'Total', total: 686.4 }, +]; + +const baseColumnOptions = { + sortable: false, + pinnable: false, + hideable: false, +}; + +const columns: GridColDef[] = [ + { + field: 'item', + headerName: 'Item/Description', + ...baseColumnOptions, + flex: 3, + colSpan: ({ row }) => { + if (row.id === 'SUBTOTAL' || row.id === 'TOTAL') { + return 3; + } + if (row.id === 'TAX') { + return 2; + } + return undefined; + }, + valueGetter: ({ value, row }) => { + if (row.id === 'SUBTOTAL' || row.id === 'TAX' || row.id === 'TOTAL') { + return row.label; + } + return value; + }, + }, + { + field: 'quantity', + headerName: 'Quantity', + ...baseColumnOptions, + flex: 1, + sortable: false, + }, + { + field: 'price', + headerName: 'Price', + flex: 1, + ...baseColumnOptions, + valueGetter: ({ row, value }) => { + if (row.id === 'TAX') { + return `${row.taxRate}%`; + } + return value; + }, + }, + { + field: 'total', + headerName: 'Total', + flex: 1, + ...baseColumnOptions, + valueGetter: ({ row }) => { + if (row.id === 'SUBTOTAL') { + return row.subtotal; + } + if (row.id === 'TAX') { + return row.taxTotal; + } + if (row.id === 'TOTAL') { + return row.total; + } + return row.price * row.quantity; + }, + }, +]; + +const getCellClassName: DataGridProps['getCellClassName'] = ({ row, field }) => { + if (row.id === 'SUBTOTAL' || row.id === 'TOTAL' || row.id === 'TAX') { + if (field === 'item') { + return 'bold'; + } + } + return ''; +}; + +export default function ColumnSpanningFunction() { + return ( + + + + ); +} diff --git a/docs/data/data-grid/column-spanning/ColumnSpanningFunction.tsx.preview b/docs/data/data-grid/column-spanning/ColumnSpanningFunction.tsx.preview new file mode 100644 index 00000000000..06433347b9b --- /dev/null +++ b/docs/data/data-grid/column-spanning/ColumnSpanningFunction.tsx.preview @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-spanning/ColumnSpanningNumber.js b/docs/data/data-grid/column-spanning/ColumnSpanningNumber.js new file mode 100644 index 00000000000..33282d88bcf --- /dev/null +++ b/docs/data/data-grid/column-spanning/ColumnSpanningNumber.js @@ -0,0 +1,32 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +const other = { + autoHeight: true, + showCellRightBorder: true, + showColumnRightBorder: true, +}; + +export default function ColumnSpanningNumber() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-spanning/ColumnSpanningNumber.tsx b/docs/data/data-grid/column-spanning/ColumnSpanningNumber.tsx new file mode 100644 index 00000000000..33282d88bcf --- /dev/null +++ b/docs/data/data-grid/column-spanning/ColumnSpanningNumber.tsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +const other = { + autoHeight: true, + showCellRightBorder: true, + showColumnRightBorder: true, +}; + +export default function ColumnSpanningNumber() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-spanning/column-spanning.md b/docs/data/data-grid/column-spanning/column-spanning.md new file mode 100644 index 00000000000..f21825b36a4 --- /dev/null +++ b/docs/data/data-grid/column-spanning/column-spanning.md @@ -0,0 +1,71 @@ +--- +title: Data Grid - Column spanning +--- + +# Data Grid - Column spanning + +

Span cells across several columns.

+ +By default, each cell takes up the width of one column. +You can modify this behavior with column spanning. +It allows cells to span multiple columns. +This is very close to the "column spanning" in an HTML ``. + +To change the number of columns a cell should span, use the `colSpan` property available in `GridColDef`: + +```ts +interface GridColDef { + /** + * Number of columns a cell should span. + * @default 1 + */ + colSpan?: number | ((params: GridCellParams) => number | undefined); + … +} +``` + +:::warning +When using `colSpan`, some other features may be pointless or may not work as expected (depending on the data model). +To avoid confusing grid layout, consider disabling the following features for the column(s) that are affected by `colSpan`: + +- [sorting](/x/react-data-grid/sorting/#disable-the-sorting) +- [filtering](/x/react-data-grid/filtering/#disable-the-filters) +- [column reorder](/x/react-data-grid/column-ordering/) +- [hiding columns](/x/react-data-grid/column-visibility/) +- [column pinning](/x/react-data-grid/column-pinning/#blocking-column-unpinning) + +::: + +## Number signature + +The number signature sets **all cells in the column** to span a given number of columns. + +```ts +interface GridColDef { + colSpan?: number; +} +``` + +{{"demo": "ColumnSpanningNumber.js", "bg": "inline"}} + +## Function signature + +The function signature allows spanning only **specific cells** in the column. +The function receives [`GridCellParams`](/api/data-grid/grid-cell-params/) as argument. + +```ts +interface GridColDef { + colSpan?: (params: GridCellParams) => number | undefined; +} +``` + +{{"demo": "ColumnSpanningFunction.js", "bg": "inline", "defaultCodeOpen": false}} + +Function signature can also be useful to derive `colSpan` value from row data: + +{{"demo": "ColumnSpanningDerived.js", "bg": "inline", "defaultCodeOpen": false}} + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/column-visibility/ColumnHiding.js b/docs/data/data-grid/column-visibility/ColumnHiding.js new file mode 100644 index 00000000000..825d0c85fb7 --- /dev/null +++ b/docs/data/data-grid/column-visibility/ColumnHiding.js @@ -0,0 +1,32 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 38, + desk: 'D-546', + }, + { + id: 2, + username: '@MUI-X', + age: 25, + desk: 'D-042', + }, +]; + +export default function ColumnHiding() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-visibility/ColumnHiding.tsx b/docs/data/data-grid/column-visibility/ColumnHiding.tsx new file mode 100644 index 00000000000..825d0c85fb7 --- /dev/null +++ b/docs/data/data-grid/column-visibility/ColumnHiding.tsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 38, + desk: 'D-546', + }, + { + id: 2, + username: '@MUI-X', + age: 25, + desk: 'D-042', + }, +]; + +export default function ColumnHiding() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-visibility/ColumnHiding.tsx.preview b/docs/data/data-grid/column-visibility/ColumnHiding.tsx.preview new file mode 100644 index 00000000000..4f84727c85f --- /dev/null +++ b/docs/data/data-grid/column-visibility/ColumnHiding.tsx.preview @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-visibility/ColumnSelectorGrid.js b/docs/data/data-grid/column-visibility/ColumnSelectorGrid.js new file mode 100644 index 00000000000..fa8928d5f5c --- /dev/null +++ b/docs/data/data-grid/column-visibility/ColumnSelectorGrid.js @@ -0,0 +1,22 @@ +import * as React from 'react'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function ColumnSelectorGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 10, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-visibility/ColumnSelectorGrid.tsx b/docs/data/data-grid/column-visibility/ColumnSelectorGrid.tsx new file mode 100644 index 00000000000..fa8928d5f5c --- /dev/null +++ b/docs/data/data-grid/column-visibility/ColumnSelectorGrid.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function ColumnSelectorGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 10, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-visibility/ColumnSelectorGrid.tsx.preview b/docs/data/data-grid/column-visibility/ColumnSelectorGrid.tsx.preview new file mode 100644 index 00000000000..02b0f196538 --- /dev/null +++ b/docs/data/data-grid/column-visibility/ColumnSelectorGrid.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-visibility/VisibleColumnsBasicExample.js b/docs/data/data-grid/column-visibility/VisibleColumnsBasicExample.js new file mode 100644 index 00000000000..df67b264e05 --- /dev/null +++ b/docs/data/data-grid/column-visibility/VisibleColumnsBasicExample.js @@ -0,0 +1,35 @@ +import * as React from 'react'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 38, + desk: 'D-546', + }, + { + id: 2, + username: '@MUI-X', + age: 25, + desk: 'D-042', + }, +]; + +export default function VisibleColumnsBasicExample() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-visibility/VisibleColumnsBasicExample.tsx b/docs/data/data-grid/column-visibility/VisibleColumnsBasicExample.tsx new file mode 100644 index 00000000000..df67b264e05 --- /dev/null +++ b/docs/data/data-grid/column-visibility/VisibleColumnsBasicExample.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; + +const rows = [ + { + id: 1, + username: '@MUI', + age: 38, + desk: 'D-546', + }, + { + id: 2, + username: '@MUI-X', + age: 25, + desk: 'D-042', + }, +]; + +export default function VisibleColumnsBasicExample() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-visibility/VisibleColumnsBasicExample.tsx.preview b/docs/data/data-grid/column-visibility/VisibleColumnsBasicExample.tsx.preview new file mode 100644 index 00000000000..cfa3c56e37b --- /dev/null +++ b/docs/data/data-grid/column-visibility/VisibleColumnsBasicExample.tsx.preview @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-visibility/VisibleColumnsModelControlled.js b/docs/data/data-grid/column-visibility/VisibleColumnsModelControlled.js new file mode 100644 index 00000000000..6d852431dbc --- /dev/null +++ b/docs/data/data-grid/column-visibility/VisibleColumnsModelControlled.js @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { DataGrid } from '@mui/x-data-grid'; + +export default function VisibleColumnsModelControlled() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 20, + maxColumns: 20, + }); + + const [columnVisibilityModel, setColumnVisibilityModel] = React.useState({ + id: false, + brokerId: false, + status: false, + }); + + return ( +
+ + setColumnVisibilityModel(newModel) + } + /> +
+ ); +} diff --git a/docs/data/data-grid/column-visibility/VisibleColumnsModelControlled.tsx b/docs/data/data-grid/column-visibility/VisibleColumnsModelControlled.tsx new file mode 100644 index 00000000000..b1fcf62f522 --- /dev/null +++ b/docs/data/data-grid/column-visibility/VisibleColumnsModelControlled.tsx @@ -0,0 +1,31 @@ +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { DataGrid, GridColumnVisibilityModel } from '@mui/x-data-grid'; + +export default function VisibleColumnsModelControlled() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 20, + maxColumns: 20, + }); + + const [columnVisibilityModel, setColumnVisibilityModel] = + React.useState({ + id: false, + brokerId: false, + status: false, + }); + + return ( +
+ + setColumnVisibilityModel(newModel) + } + /> +
+ ); +} diff --git a/docs/data/data-grid/column-visibility/VisibleColumnsModelControlled.tsx.preview b/docs/data/data-grid/column-visibility/VisibleColumnsModelControlled.tsx.preview new file mode 100644 index 00000000000..ccf5bd1af90 --- /dev/null +++ b/docs/data/data-grid/column-visibility/VisibleColumnsModelControlled.tsx.preview @@ -0,0 +1,8 @@ + + setColumnVisibilityModel(newModel) + } +/> \ No newline at end of file diff --git a/docs/data/data-grid/column-visibility/VisibleColumnsModelInitialState.js b/docs/data/data-grid/column-visibility/VisibleColumnsModelInitialState.js new file mode 100644 index 00000000000..1a618ce26bc --- /dev/null +++ b/docs/data/data-grid/column-visibility/VisibleColumnsModelInitialState.js @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { DataGrid } from '@mui/x-data-grid'; + +export default function VisibleColumnsModelInitialState() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 20, + maxColumns: 20, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-visibility/VisibleColumnsModelInitialState.tsx b/docs/data/data-grid/column-visibility/VisibleColumnsModelInitialState.tsx new file mode 100644 index 00000000000..1a618ce26bc --- /dev/null +++ b/docs/data/data-grid/column-visibility/VisibleColumnsModelInitialState.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { DataGrid } from '@mui/x-data-grid'; + +export default function VisibleColumnsModelInitialState() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 20, + maxColumns: 20, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/column-visibility/VisibleColumnsModelInitialState.tsx.preview b/docs/data/data-grid/column-visibility/VisibleColumnsModelInitialState.tsx.preview new file mode 100644 index 00000000000..ca17f502cb1 --- /dev/null +++ b/docs/data/data-grid/column-visibility/VisibleColumnsModelInitialState.tsx.preview @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/column-visibility/column-visibility.md b/docs/data/data-grid/column-visibility/column-visibility.md new file mode 100644 index 00000000000..ef4cdbbe98c --- /dev/null +++ b/docs/data/data-grid/column-visibility/column-visibility.md @@ -0,0 +1,87 @@ +--- +title: Data Grid - Column visibility +--- + +# Data Grid - Column visibility + +

Define which columns are visible.

+ +By default, all the columns are visible. +The column's visibility can be switched through the user interface in two ways: + +- By opening the column menu and clicking the _Hide_ menu item. +- By clicking the _Columns_ menu and toggling the columns to show or hide. + +You can prevent the user from hiding a column through the user interface by setting the `hideable` in `GridColDef` to `false`. + +In the following demo, the "username" column cannot be hidden. + +{{"demo": "VisibleColumnsBasicExample.js", "bg": "inline"}} + +## Initialize the visible columns + +To initialize the visible columns without controlling them, provide the model to the `initialState` prop. + +```tsx + +``` + +{{"demo": "VisibleColumnsModelInitialState.js", "bg": "inline", "defaultCodeOpen": false }} + +## Controlled visible columns + +Use the `columnVisibilityModel` prop to control the visible columns. +You can use the `onColumnVisibilityModelChange` prop to listen to the changes to the visible columns and update the prop accordingly. + +```tsx + +``` + +:::warning +The grid does not handle switching between controlled and uncontrolled modes. + +This edge case will be supported in v6 after the removal of the legacy `hide` field. +::: + +{{"demo": "VisibleColumnsModelControlled.js", "bg": "inline"}} + +## Column visibility panel + +The column visibility panel can be opened through the grid toolbar. +To enable it, you need to add `Toolbar: GridToolbar` to the grid `components` prop. + +The user can then choose which columns are visible using the _Columns_ button. + +{{"demo": "ColumnSelectorGrid.js", "bg": "inline"}} + +:::info +To hide the column visibility panel from the toolbar, set the prop `disableColumnSelector={true}`. +::: + +## Column `hide` property (deprecated) + +Before the introduction of the `columnVisibilityModel`, the columns could be hidden by setting the `hide` property in `GridColDef` to `true`. +This method still works but will be removed on the next major release. + +{{"demo": "ColumnHiding.js", "bg": "inline"}} + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/components/CellWithPopover.js b/docs/data/data-grid/components/CellWithPopover.js new file mode 100644 index 00000000000..a3186bbe719 --- /dev/null +++ b/docs/data/data-grid/components/CellWithPopover.js @@ -0,0 +1,63 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Popover from '@mui/material/Popover'; +import Typography from '@mui/material/Typography'; + +export default function CellWithPopover() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 4, + }); + + const [anchorEl, setAnchorEl] = React.useState(null); + const [value, setValue] = React.useState(''); + + const handlePopoverOpen = (event) => { + const field = event.currentTarget.dataset.field; + const id = event.currentTarget.parentElement.dataset.id; + const row = data.rows.find((r) => r.id === id); + setValue(row[field]); + setAnchorEl(event.currentTarget); + }; + + const handlePopoverClose = () => { + setAnchorEl(null); + }; + + const open = Boolean(anchorEl); + + return ( +
+ + + {`${value.length} characters.`} + +
+ ); +} diff --git a/docs/data/data-grid/components/CellWithPopover.tsx b/docs/data/data-grid/components/CellWithPopover.tsx new file mode 100644 index 00000000000..49b41736796 --- /dev/null +++ b/docs/data/data-grid/components/CellWithPopover.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Popover from '@mui/material/Popover'; +import Typography from '@mui/material/Typography'; + +export default function CellWithPopover() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 4, + }); + const [anchorEl, setAnchorEl] = React.useState(null); + const [value, setValue] = React.useState(''); + + const handlePopoverOpen = (event: React.MouseEvent) => { + const field = event.currentTarget.dataset.field!; + const id = event.currentTarget.parentElement!.dataset.id!; + const row = data.rows.find((r) => r.id === id)!; + setValue(row[field]); + setAnchorEl(event.currentTarget); + }; + + const handlePopoverClose = () => { + setAnchorEl(null); + }; + + const open = Boolean(anchorEl); + + return ( +
+ + + {`${value.length} characters.`} + +
+ ); +} diff --git a/docs/data/data-grid/components/CustomColumnMenu.js b/docs/data/data-grid/components/CustomColumnMenu.js new file mode 100644 index 00000000000..2ad13bc6349 --- /dev/null +++ b/docs/data/data-grid/components/CustomColumnMenu.js @@ -0,0 +1,138 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import { styled } from '@mui/material/styles'; +import Button from '@mui/material/Button'; +import { + GridColumnMenu, + GridColumnMenuContainer, + GridFilterMenuItem, + SortGridMenuItems, + useGridApiRef, + DataGridPro, +} from '@mui/x-data-grid-pro'; +import StarOutlineIcon from '@mui/icons-material/StarOutline'; + +const StyledGridColumnMenuContainer = styled(GridColumnMenuContainer)( + ({ theme, ownerState }) => ({ + background: theme.palette[ownerState.color].main, + color: theme.palette[ownerState.color].contrastText, + }), +); + +const StyledGridColumnMenu = styled(GridColumnMenu)(({ theme, ownerState }) => ({ + background: theme.palette[ownerState.color].main, + color: theme.palette[ownerState.color].contrastText, +})); + +function CustomColumnMenuComponent(props) { + const { hideMenu, currentColumn, color, ...other } = props; + + if (currentColumn.field === 'name') { + return ( + + + + + ); + } + if (currentColumn.field === 'stars') { + return ( + + + + + + ); + } + return ( + + ); +} + +CustomColumnMenuComponent.propTypes = { + color: PropTypes.oneOf(['primary', 'secondary']).isRequired, + currentColumn: PropTypes.object.isRequired, + hideMenu: PropTypes.func.isRequired, +}; + +export { CustomColumnMenuComponent }; + +export default function CustomColumnMenu() { + const [color, setColor] = React.useState('primary'); + const apiRef = useGridApiRef(); + + return ( +
+ +
+ +
+
+ ); +} diff --git a/docs/data/data-grid/components/CustomColumnMenu.tsx b/docs/data/data-grid/components/CustomColumnMenu.tsx new file mode 100644 index 00000000000..0fde3457f95 --- /dev/null +++ b/docs/data/data-grid/components/CustomColumnMenu.tsx @@ -0,0 +1,137 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { styled, Theme } from '@mui/material/styles'; +import Button from '@mui/material/Button'; +import { + GridColumnMenu, + GridColumnMenuContainer, + GridColumnMenuProps, + GridFilterMenuItem, + SortGridMenuItems, + useGridApiRef, + DataGridPro, +} from '@mui/x-data-grid-pro'; +import StarOutlineIcon from '@mui/icons-material/StarOutline'; + +type PaletteColorKey = 'primary' | 'secondary'; +type OwnerState = { + color: PaletteColorKey; +}; + +const StyledGridColumnMenuContainer = styled(GridColumnMenuContainer)<{ + ownerState: OwnerState; +}>(({ theme, ownerState }: { theme: Theme; ownerState: OwnerState }) => ({ + background: theme.palette[ownerState.color].main, + color: theme.palette[ownerState.color].contrastText, +})); + +const StyledGridColumnMenu = styled(GridColumnMenu)<{ + ownerState: OwnerState; +}>(({ theme, ownerState }: { theme: Theme; ownerState: OwnerState }) => ({ + background: theme.palette[ownerState.color].main, + color: theme.palette[ownerState.color].contrastText, +})); + +export function CustomColumnMenuComponent(props: GridColumnMenuProps & OwnerState) { + const { hideMenu, currentColumn, color, ...other } = props; + + if (currentColumn.field === 'name') { + return ( + + + + + ); + } + if (currentColumn.field === 'stars') { + return ( + + + + + + ); + } + return ( + + ); +} + +export default function CustomColumnMenu() { + const [color, setColor] = React.useState('primary'); + const apiRef = useGridApiRef(); + + return ( +
+ +
+ +
+
+ ); +} diff --git a/docs/data/data-grid/components/CustomEmptyOverlayGrid.js b/docs/data/data-grid/components/CustomEmptyOverlayGrid.js new file mode 100644 index 00000000000..3c2f8891dd3 --- /dev/null +++ b/docs/data/data-grid/components/CustomEmptyOverlayGrid.js @@ -0,0 +1,96 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { styled } from '@mui/material/styles'; + +const StyledGridOverlay = styled('div')(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + height: '100%', + '& .ant-empty-img-1': { + fill: theme.palette.mode === 'light' ? '#aeb8c2' : '#262626', + }, + '& .ant-empty-img-2': { + fill: theme.palette.mode === 'light' ? '#f5f5f7' : '#595959', + }, + '& .ant-empty-img-3': { + fill: theme.palette.mode === 'light' ? '#dce0e6' : '#434343', + }, + '& .ant-empty-img-4': { + fill: theme.palette.mode === 'light' ? '#fff' : '#1c1c1c', + }, + '& .ant-empty-img-5': { + fillOpacity: theme.palette.mode === 'light' ? '0.8' : '0.08', + fill: theme.palette.mode === 'light' ? '#f5f5f5' : '#fff', + }, +})); + +function CustomNoRowsOverlay() { + return ( + + + + + + + + + + + + + + + + + No Rows + + ); +} + +export default function CustomEmptyOverlayGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/CustomEmptyOverlayGrid.tsx b/docs/data/data-grid/components/CustomEmptyOverlayGrid.tsx new file mode 100644 index 00000000000..3c2f8891dd3 --- /dev/null +++ b/docs/data/data-grid/components/CustomEmptyOverlayGrid.tsx @@ -0,0 +1,96 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { styled } from '@mui/material/styles'; + +const StyledGridOverlay = styled('div')(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + height: '100%', + '& .ant-empty-img-1': { + fill: theme.palette.mode === 'light' ? '#aeb8c2' : '#262626', + }, + '& .ant-empty-img-2': { + fill: theme.palette.mode === 'light' ? '#f5f5f7' : '#595959', + }, + '& .ant-empty-img-3': { + fill: theme.palette.mode === 'light' ? '#dce0e6' : '#434343', + }, + '& .ant-empty-img-4': { + fill: theme.palette.mode === 'light' ? '#fff' : '#1c1c1c', + }, + '& .ant-empty-img-5': { + fillOpacity: theme.palette.mode === 'light' ? '0.8' : '0.08', + fill: theme.palette.mode === 'light' ? '#f5f5f5' : '#fff', + }, +})); + +function CustomNoRowsOverlay() { + return ( + + + + + + + + + + + + + + + + + No Rows + + ); +} + +export default function CustomEmptyOverlayGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/CustomEmptyOverlayGrid.tsx.preview b/docs/data/data-grid/components/CustomEmptyOverlayGrid.tsx.preview new file mode 100644 index 00000000000..3ca62e8e772 --- /dev/null +++ b/docs/data/data-grid/components/CustomEmptyOverlayGrid.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/components/CustomFooter.js b/docs/data/data-grid/components/CustomFooter.js new file mode 100644 index 00000000000..1b6ababb1c2 --- /dev/null +++ b/docs/data/data-grid/components/CustomFooter.js @@ -0,0 +1,64 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { DataGrid } from '@mui/x-data-grid'; +import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord'; + +function CustomFooterStatusComponent(props) { + return ( + + + Status {props.status} + + ); +} + +CustomFooterStatusComponent.propTypes = { + status: PropTypes.oneOf(['connected', 'disconnected']).isRequired, +}; + +export { CustomFooterStatusComponent }; + +export default function CustomFooter() { + const [status, setStatus] = React.useState('connected'); + const { data } = useDemoData({ + dataSet: 'Employee', + rowLength: 4, + maxColumns: 6, + }); + + return ( + + + + + + + ); +} diff --git a/docs/data/data-grid/components/CustomFooter.tsx b/docs/data/data-grid/components/CustomFooter.tsx new file mode 100644 index 00000000000..bc4c2303f52 --- /dev/null +++ b/docs/data/data-grid/components/CustomFooter.tsx @@ -0,0 +1,58 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { DataGrid } from '@mui/x-data-grid'; +import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord'; + +export function CustomFooterStatusComponent(props: { + status: 'connected' | 'disconnected'; +}) { + return ( + + + Status {props.status} + + ); +} + +export default function CustomFooter() { + const [status, setStatus] = React.useState('connected'); + const { data } = useDemoData({ + dataSet: 'Employee', + rowLength: 4, + maxColumns: 6, + }); + return ( + + + + + + + ); +} diff --git a/docs/data/data-grid/components/CustomLoadingOverlayGrid.js b/docs/data/data-grid/components/CustomLoadingOverlayGrid.js new file mode 100644 index 00000000000..5d3dd060123 --- /dev/null +++ b/docs/data/data-grid/components/CustomLoadingOverlayGrid.js @@ -0,0 +1,24 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import LinearProgress from '@mui/material/LinearProgress'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function CustomLoadingOverlayGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/CustomLoadingOverlayGrid.tsx b/docs/data/data-grid/components/CustomLoadingOverlayGrid.tsx new file mode 100644 index 00000000000..5d3dd060123 --- /dev/null +++ b/docs/data/data-grid/components/CustomLoadingOverlayGrid.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import LinearProgress from '@mui/material/LinearProgress'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function CustomLoadingOverlayGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/CustomLoadingOverlayGrid.tsx.preview b/docs/data/data-grid/components/CustomLoadingOverlayGrid.tsx.preview new file mode 100644 index 00000000000..b1ce0c3d46c --- /dev/null +++ b/docs/data/data-grid/components/CustomLoadingOverlayGrid.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/components/CustomPaginationGrid.js b/docs/data/data-grid/components/CustomPaginationGrid.js new file mode 100644 index 00000000000..16837702bca --- /dev/null +++ b/docs/data/data-grid/components/CustomPaginationGrid.js @@ -0,0 +1,48 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { + DataGrid, + gridPageCountSelector, + gridPageSelector, + useGridApiContext, + useGridSelector, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Pagination from '@mui/material/Pagination'; + +function CustomPagination() { + const apiRef = useGridApiContext(); + const page = useGridSelector(apiRef, gridPageSelector); + const pageCount = useGridSelector(apiRef, gridPageCountSelector); + + return ( + apiRef.current.setPage(value - 1)} + /> + ); +} + +export default function CustomPaginationGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 6, + }); + + return ( + + + + ); +} diff --git a/docs/data/data-grid/components/CustomPaginationGrid.tsx b/docs/data/data-grid/components/CustomPaginationGrid.tsx new file mode 100644 index 00000000000..16837702bca --- /dev/null +++ b/docs/data/data-grid/components/CustomPaginationGrid.tsx @@ -0,0 +1,48 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { + DataGrid, + gridPageCountSelector, + gridPageSelector, + useGridApiContext, + useGridSelector, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Pagination from '@mui/material/Pagination'; + +function CustomPagination() { + const apiRef = useGridApiContext(); + const page = useGridSelector(apiRef, gridPageSelector); + const pageCount = useGridSelector(apiRef, gridPageCountSelector); + + return ( + apiRef.current.setPage(value - 1)} + /> + ); +} + +export default function CustomPaginationGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 6, + }); + + return ( + + + + ); +} diff --git a/docs/data/data-grid/components/CustomPaginationGrid.tsx.preview b/docs/data/data-grid/components/CustomPaginationGrid.tsx.preview new file mode 100644 index 00000000000..545731845c4 --- /dev/null +++ b/docs/data/data-grid/components/CustomPaginationGrid.tsx.preview @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/components/CustomSortIcons.js b/docs/data/data-grid/components/CustomSortIcons.js new file mode 100644 index 00000000000..6d034f4f929 --- /dev/null +++ b/docs/data/data-grid/components/CustomSortIcons.js @@ -0,0 +1,46 @@ +import * as React from 'react'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import { DataGrid } from '@mui/x-data-grid'; + +export function SortedDescendingIcon() { + return ; +} + +export function SortedAscendingIcon() { + return ; +} + +const rows = [ + { + id: 1, + name: 'MUI', + stars: 28000, + }, + { + id: 2, + name: 'DataGrid', + stars: 15000, + }, +]; + +const columns = [ + { field: 'name', width: 150 }, + { field: 'stars', width: 150 }, +]; + +export default function CustomSortIcons() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/CustomSortIcons.tsx b/docs/data/data-grid/components/CustomSortIcons.tsx new file mode 100644 index 00000000000..6d034f4f929 --- /dev/null +++ b/docs/data/data-grid/components/CustomSortIcons.tsx @@ -0,0 +1,46 @@ +import * as React from 'react'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import { DataGrid } from '@mui/x-data-grid'; + +export function SortedDescendingIcon() { + return ; +} + +export function SortedAscendingIcon() { + return ; +} + +const rows = [ + { + id: 1, + name: 'MUI', + stars: 28000, + }, + { + id: 2, + name: 'DataGrid', + stars: 15000, + }, +]; + +const columns = [ + { field: 'name', width: 150 }, + { field: 'stars', width: 150 }, +]; + +export default function CustomSortIcons() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/CustomSortIcons.tsx.preview b/docs/data/data-grid/components/CustomSortIcons.tsx.preview new file mode 100644 index 00000000000..f136d4a699e --- /dev/null +++ b/docs/data/data-grid/components/CustomSortIcons.tsx.preview @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/components/CustomToolbarGrid.js b/docs/data/data-grid/components/CustomToolbarGrid.js new file mode 100644 index 00000000000..3352e3851ad --- /dev/null +++ b/docs/data/data-grid/components/CustomToolbarGrid.js @@ -0,0 +1,40 @@ +import * as React from 'react'; +import { + DataGrid, + GridToolbarContainer, + GridToolbarColumnsButton, + GridToolbarFilterButton, + GridToolbarExport, + GridToolbarDensitySelector, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function CustomToolbar() { + return ( + + + + + + + ); +} + +export default function CustomToolbarGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/CustomToolbarGrid.tsx b/docs/data/data-grid/components/CustomToolbarGrid.tsx new file mode 100644 index 00000000000..3352e3851ad --- /dev/null +++ b/docs/data/data-grid/components/CustomToolbarGrid.tsx @@ -0,0 +1,40 @@ +import * as React from 'react'; +import { + DataGrid, + GridToolbarContainer, + GridToolbarColumnsButton, + GridToolbarFilterButton, + GridToolbarExport, + GridToolbarDensitySelector, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function CustomToolbar() { + return ( + + + + + + + ); +} + +export default function CustomToolbarGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/CustomToolbarGrid.tsx.preview b/docs/data/data-grid/components/CustomToolbarGrid.tsx.preview new file mode 100644 index 00000000000..9a7e9e10106 --- /dev/null +++ b/docs/data/data-grid/components/CustomToolbarGrid.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/components/RowContextMenu.js b/docs/data/data-grid/components/RowContextMenu.js new file mode 100644 index 00000000000..d05d96e9dc4 --- /dev/null +++ b/docs/data/data-grid/components/RowContextMenu.js @@ -0,0 +1,122 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; + +const columns = [ + { + field: 'first', + headerName: 'First', + width: 140, + }, + { + field: 'last', + headerName: 'Last', + width: 140, + }, +]; + +const initialRows = [ + { + id: 1, + first: 'Jane', + last: 'Carter', + }, + { + id: 2, + first: 'Jack', + last: 'Smith', + }, + { + id: 3, + first: 'Gill', + last: 'Martin', + }, +]; + +export default function RowContextMenu() { + const [rows, setRows] = React.useState(initialRows); + const [selectedRow, setSelectedRow] = React.useState(); + + const [contextMenu, setContextMenu] = React.useState(null); + + const handleContextMenu = (event) => { + event.preventDefault(); + setSelectedRow(Number(event.currentTarget.getAttribute('data-id'))); + setContextMenu( + contextMenu === null + ? { mouseX: event.clientX - 2, mouseY: event.clientY - 4 } + : null, + ); + }; + + const handleClose = () => { + setContextMenu(null); + }; + + const convertToUppercase = () => { + const newRows = rows.map((row) => { + if (row.id === selectedRow) { + return { + ...row, + first: row.first.toUpperCase(), + last: row.last.toUpperCase(), + }; + } + return row; + }); + setRows(newRows); + handleClose(); + }; + + const convertToLowercase = () => { + const newRows = rows.map((row) => { + if (row.id === selectedRow) { + return { + ...row, + first: row.first.toLowerCase(), + last: row.last.toLowerCase(), + }; + } + return row; + }); + setRows(newRows); + handleClose(); + }; + + return ( +
+ + { + e.preventDefault(); + handleClose(); + }, + }, + }} + > + UPPERCASE + lowercase + +
+ ); +} diff --git a/docs/data/data-grid/components/RowContextMenu.tsx b/docs/data/data-grid/components/RowContextMenu.tsx new file mode 100644 index 00000000000..1f413e7466c --- /dev/null +++ b/docs/data/data-grid/components/RowContextMenu.tsx @@ -0,0 +1,125 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; + +const columns = [ + { + field: 'first', + headerName: 'First', + width: 140, + }, + { + field: 'last', + headerName: 'Last', + width: 140, + }, +]; + +const initialRows = [ + { + id: 1, + first: 'Jane', + last: 'Carter', + }, + { + id: 2, + first: 'Jack', + last: 'Smith', + }, + { + id: 3, + first: 'Gill', + last: 'Martin', + }, +]; + +export default function RowContextMenu() { + const [rows, setRows] = React.useState(initialRows); + const [selectedRow, setSelectedRow] = React.useState(); + + const [contextMenu, setContextMenu] = React.useState<{ + mouseX: number; + mouseY: number; + } | null>(null); + + const handleContextMenu = (event: React.MouseEvent) => { + event.preventDefault(); + setSelectedRow(Number(event.currentTarget.getAttribute('data-id'))); + setContextMenu( + contextMenu === null + ? { mouseX: event.clientX - 2, mouseY: event.clientY - 4 } + : null, + ); + }; + + const handleClose = () => { + setContextMenu(null); + }; + + const convertToUppercase = () => { + const newRows = rows.map((row) => { + if (row.id === selectedRow) { + return { + ...row, + first: row.first.toUpperCase(), + last: row.last.toUpperCase(), + }; + } + return row; + }); + setRows(newRows); + handleClose(); + }; + + const convertToLowercase = () => { + const newRows = rows.map((row) => { + if (row.id === selectedRow) { + return { + ...row, + first: row.first.toLowerCase(), + last: row.last.toLowerCase(), + }; + } + return row; + }); + setRows(newRows); + handleClose(); + }; + + return ( +
+ + { + e.preventDefault(); + handleClose(); + }, + }, + }} + > + UPPERCASE + lowercase + +
+ ); +} diff --git a/docs/data/data-grid/components/ToolbarGrid.js b/docs/data/data-grid/components/ToolbarGrid.js new file mode 100644 index 00000000000..d4e45d41b8a --- /dev/null +++ b/docs/data/data-grid/components/ToolbarGrid.js @@ -0,0 +1,22 @@ +import * as React from 'react'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function ToolbarGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/ToolbarGrid.tsx b/docs/data/data-grid/components/ToolbarGrid.tsx new file mode 100644 index 00000000000..d4e45d41b8a --- /dev/null +++ b/docs/data/data-grid/components/ToolbarGrid.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function ToolbarGrid() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/ToolbarGrid.tsx.preview b/docs/data/data-grid/components/ToolbarGrid.tsx.preview new file mode 100644 index 00000000000..02b0f196538 --- /dev/null +++ b/docs/data/data-grid/components/ToolbarGrid.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/components/components.md b/docs/data/data-grid/components/components.md new file mode 100644 index 00000000000..605cf6713b2 --- /dev/null +++ b/docs/data/data-grid/components/components.md @@ -0,0 +1,176 @@ +--- +title: Data Grid - Components +--- + +# Data Grid - Components + +

The grid is highly customizable. Override components using the components prop.

+ +## Overriding components + +As part of the customization API, the grid allows you to override internal components with the `components` prop. +The prop accepts an object of type [`GridSlotsComponent`](/x/api/data-grid/data-grid/#slots). + +If you wish to pass additional props in a component slot, you can do it using the `componentsProps` prop. +This prop is of type `GridSlotsComponentsProps`. + +As an example, you could override the column menu and pass additional props as below. + +```jsx + +``` + +:::info +The casing is different between the `components` (ColumnMenu) and `componentsProps` (columnMenu) props. +::: + +### Interacting with the grid + +The grid exposes two hooks to help you to access the grid data while overriding component slots. + +They can be used as below: + +- `useGridApiContext`: returns the `apiRef`. +- `useGridSelector`: returns the result of a selector on the current state. + +More details about the selectors in the [State page](/x/react-data-grid/state/#access-the-state) + +```tsx +function CustomPagination() { + const apiRef = useGridApiContext(); + const page = useGridSelector(apiRef, gridPageSelector); + const pageCount = useGridSelector(apiRef, gridPageCountSelector); + + return ( + apiRef.current.setPage(value - 1)} + /> + ); +} +``` + +## Components + +The full list of overridable components can be found on the [`GridSlotsComponent`](/x/api/data-grid/data-grid/#slots) API page. + +### Column menu + +As mentioned above, the column menu is a component slot that can be recomposed easily and customized on each column as in the demo below. + +{{"demo": "CustomColumnMenu.js", "bg": "inline"}} + +Below is the default `GridColumnMenu`. + +```tsx +export const GridColumnMenu = React.forwardRef< + HTMLUListElement, + GridColumnMenuProps +>(function GridColumnMenu(props: GridColumnMenuProps, ref) { + const { hideMenu, currentColumn } = props; + + return ( + + + + + + + ); +}); +``` + +### Toolbar + +To enable the toolbar you need to add the `Toolbar: GridToolbar` to the grid `components` prop. +This demo showcases how this can be achieved. + +{{"demo": "ToolbarGrid.js", "bg": "inline"}} + +Alternatively, you can compose your own toolbar. + +```jsx +function CustomToolbar() { + return ( + + + + + + + ); +} +``` + +{{"demo": "CustomToolbarGrid.js", "bg": "inline"}} + +### Footer + +The grid exposes props to hide specific elements of the UI: + +- `hideFooter`: If `true`, the footer component is hidden. +- `hideFooterRowCount`: If `true`, the row count in the footer is hidden. +- `hideFooterSelectedRowCount`: If `true`, the selected row count in the footer is hidden. +- `hideFooterPagination`: If `true`, the pagination component in the footer is hidden. + +{{"demo": "CustomFooter.js", "bg": "inline"}} + +### Pagination + +By default, pagination uses the [TablePagination](/material-ui/react-pagination/#table-pagination) component that is optimized for handling tabular data. +This demo replaces it with the [Pagination](/material-ui/react-pagination/) component. + +{{"demo": "CustomPaginationGrid.js", "bg": "inline"}} + +### Loading overlay + +By default, the loading overlay displays a circular progress. +This demo replaces it with a linear progress. + +{{"demo": "CustomLoadingOverlayGrid.js", "bg": "inline"}} + +### No rows overlay + +In the following demo, an illustration is added on top of the default "No Rows" message. + +{{"demo": "CustomEmptyOverlayGrid.js", "bg": "inline"}} + +:::info +As the no rows overlay, the grid allows to override the no results overlay with the `NoResultsOverlay` slot. +::: + +### Row + +The `componentsProps.row` prop can be used to pass additional props to the row component. +One common use case might be to listen for events not exposed by [default](/x/react-data-grid/events/#catalog-of-events). +The demo below shows a context menu when a row is right-clicked. + +{{"demo": "RowContextMenu.js", "bg": "inline"}} + +### Cell + +The following demo uses the `componentsProps.cell` prop to listen for specific events emitted by the cells. +Try it by hovering a cell with the mouse and it should display the number of characters each cell has. + +{{"demo": "CellWithPopover.js", "bg": "inline"}} + +### Icons + +As any component slot, every icon can be customized. However, it is not yet possible to use the `componentsProps` with icons. + +{{"demo": "CustomSortIcons.js", "bg": "inline"}} + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/demo/FullFeaturedDemo.js b/docs/data/data-grid/demo/FullFeaturedDemo.js new file mode 100644 index 00000000000..aee00ea34f5 --- /dev/null +++ b/docs/data/data-grid/demo/FullFeaturedDemo.js @@ -0,0 +1,323 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import { DataGridPro, GridToolbar } from '@mui/x-data-grid-pro'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import { styled } from '@mui/material/styles'; +import FormControl from '@mui/material/FormControl'; +import FormGroup from '@mui/material/FormGroup'; +import Button from '@mui/material/Button'; +import InputLabel from '@mui/material/InputLabel'; +import MenuItem from '@mui/material/MenuItem'; +import Select from '@mui/material/Select'; + +const AntDesignStyledDataGridPro = styled(DataGridPro)(({ theme }) => ({ + border: `1px solid ${theme.palette.mode === 'light' ? '#f0f0f0' : '#303030'}`, + color: + theme.palette.mode === 'light' ? 'rgba(0,0,0,.85)' : 'rgba(255,255,255,0.85)', + fontFamily: [ + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', + 'Roboto', + '"Helvetica Neue"', + 'Arial', + 'sans-serif', + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"', + ].join(','), + WebkitFontSmoothing: 'auto', + letterSpacing: 'normal', + '& .MuiDataGrid-columnsContainer': { + backgroundColor: theme.palette.mode === 'light' ? '#fafafa' : '#1d1d1d', + }, + '& .MuiDataGrid-iconSeparator': { + display: 'none', + }, + '& .MuiDataGrid-columnHeader, .MuiDataGrid-cell': { + borderRight: `1px solid ${ + theme.palette.mode === 'light' ? '#f0f0f0' : '#303030' + }`, + }, + '& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell': { + borderBottom: `1px solid ${ + theme.palette.mode === 'light' ? '#f0f0f0' : '#303030' + }`, + }, + '& .MuiDataGrid-cell': { + color: + theme.palette.mode === 'light' ? 'rgba(0,0,0,.85)' : 'rgba(255,255,255,0.85)', + fontFamily: [ + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', + 'Roboto', + '"Helvetica Neue"', + 'Arial', + 'sans-serif', + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"', + ].join(','), + WebkitFontSmoothing: 'auto', + letterSpacing: 'normal', + '& .MuiDataGrid-columnsContainer': { + backgroundColor: theme.palette.mode === 'light' ? '#fafafa' : '#1d1d1d', + }, + '& .MuiDataGrid-iconSeparator': { + display: 'none', + }, + '& .MuiDataGrid-colCell, .MuiDataGrid-cell': { + borderRight: `1px solid ${ + theme.palette.mode === 'light' ? '#f0f0f0' : '#303030' + }`, + }, + '& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell': { + borderBottom: `1px solid ${ + theme.palette.mode === 'light' ? '#f0f0f0' : '#303030' + }`, + }, + '& .MuiDataGrid-cell': { + color: + theme.palette.mode === 'light' + ? 'rgba(0,0,0,.85)' + : 'rgba(255,255,255,0.65)', + }, + '& .MuiPaginationItem-root': { + borderRadius: 0, + }, + '& .MuiCheckbox-root svg': { + width: 16, + height: 16, + backgroundColor: 'transparent', + border: `1px solid ${ + theme.palette.mode === 'light' ? '#d9d9d9' : 'rgb(67, 67, 67)' + }`, + borderRadius: 2, + }, + '& .MuiCheckbox-root svg path': { + display: 'none', + }, + '& .MuiCheckbox-root.Mui-checked:not(.MuiCheckbox-indeterminate) svg': { + backgroundColor: '#1890ff', + borderColor: '#1890ff', + }, + '& .MuiCheckbox-root.Mui-checked .MuiIconButton-label:after': { + position: 'absolute', + display: 'table', + border: '2px solid #fff', + borderTop: 0, + borderLeft: 0, + transform: 'rotate(45deg) translate(-50%,-50%)', + opacity: 1, + transition: 'all .2s cubic-bezier(.12,.4,.29,1.46) .1s', + content: '""', + top: '50%', + left: '39%', + width: 5.71428571, + height: 9.14285714, + }, + '& .MuiCheckbox-root.MuiCheckbox-indeterminate .MuiIconButton-label:after': { + width: 8, + height: 8, + backgroundColor: '#1890ff', + transform: 'none', + top: '39%', + border: 0, + }, + }, +})); + +const StyledBox = styled(Box)(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + height: 600, + width: '100%', + '& .MuiFormGroup-options': { + alignItems: 'center', + paddingBottom: theme.spacing(1), + '& > div': { + minWidth: 100, + margin: theme.spacing(2), + marginLeft: 0, + }, + }, +})); + +function SettingsPanel(props) { + const { onApply, type, size, theme } = props; + const [sizeState, setSize] = React.useState(size); + const [typeState, setType] = React.useState(type); + const [selectedPaginationValue, setSelectedPaginationValue] = React.useState(-1); + const [activeTheme, setActiveTheme] = React.useState(theme); + + const handleSizeChange = React.useCallback((event) => { + setSize(Number(event.target.value)); + }, []); + + const handleDatasetChange = React.useCallback((event) => { + setType(event.target.value); + }, []); + + const handlePaginationChange = React.useCallback((event) => { + setSelectedPaginationValue(event.target.value); + }, []); + + const handleThemeChange = React.useCallback((event) => { + setActiveTheme(event.target.value); + }, []); + + const handleApplyChanges = React.useCallback(() => { + onApply({ + size: sizeState, + type: typeState, + pagesize: selectedPaginationValue, + theme: activeTheme, + }); + }, [sizeState, typeState, selectedPaginationValue, activeTheme, onApply]); + + return ( + + + Dataset + + + + Rows + + + + Page Size + + + + Theme + + + + + ); +} + +SettingsPanel.propTypes = { + onApply: PropTypes.func.isRequired, + size: PropTypes.number.isRequired, + theme: PropTypes.oneOf(['ant', 'default']).isRequired, + type: PropTypes.oneOf(['Commodity', 'Employee']).isRequired, +}; + +export default function FullFeaturedDemo() { + const [isAntDesign, setIsAntDesign] = React.useState(false); + const [type, setType] = React.useState('Commodity'); + const [size, setSize] = React.useState(100); + const { loading, data, setRowLength, loadNewData } = useDemoData({ + dataSet: type, + rowLength: size, + maxColumns: 40, + editable: true, + }); + + const [pagination, setPagination] = React.useState({ + pagination: false, + autoPageSize: false, + pageSize: undefined, + }); + + const getActiveTheme = () => { + return isAntDesign ? 'ant' : 'default'; + }; + + const handleApplyClick = (settings) => { + if (size !== settings.size) { + setSize(settings.size); + } + + if (type !== settings.type) { + setType(settings.type); + } + + if (getActiveTheme() !== settings.theme) { + setIsAntDesign(!isAntDesign); + } + + if (size !== settings.size || type !== settings.type) { + setRowLength(settings.size); + loadNewData(); + } + + const newPaginationSettings = { + pagination: settings.pagesize !== -1, + autoPageSize: settings.pagesize === 0, + pageSize: settings.pagesize > 0 ? settings.pagesize : undefined, + }; + + setPagination((currentPaginationSettings) => { + if ( + currentPaginationSettings.pagination === newPaginationSettings.pagination && + currentPaginationSettings.autoPageSize === + newPaginationSettings.autoPageSize && + currentPaginationSettings.pageSize === newPaginationSettings.pageSize + ) { + return currentPaginationSettings; + } + return newPaginationSettings; + }); + }; + + const DataGridComponent = isAntDesign ? AntDesignStyledDataGridPro : DataGridPro; + + return ( + + + + + ); +} diff --git a/docs/data/data-grid/demo/FullFeaturedDemo.tsx b/docs/data/data-grid/demo/FullFeaturedDemo.tsx new file mode 100644 index 00000000000..939bb28089d --- /dev/null +++ b/docs/data/data-grid/demo/FullFeaturedDemo.tsx @@ -0,0 +1,344 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGridPro, GridToolbar } from '@mui/x-data-grid-pro'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import { styled } from '@mui/material/styles'; +import FormControl from '@mui/material/FormControl'; +import FormGroup from '@mui/material/FormGroup'; +import Button from '@mui/material/Button'; +import InputLabel from '@mui/material/InputLabel'; +import MenuItem from '@mui/material/MenuItem'; +import Select from '@mui/material/Select'; + +const AntDesignStyledDataGridPro = styled(DataGridPro)(({ theme }) => ({ + border: `1px solid ${theme.palette.mode === 'light' ? '#f0f0f0' : '#303030'}`, + color: + theme.palette.mode === 'light' ? 'rgba(0,0,0,.85)' : 'rgba(255,255,255,0.85)', + fontFamily: [ + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', + 'Roboto', + '"Helvetica Neue"', + 'Arial', + 'sans-serif', + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"', + ].join(','), + WebkitFontSmoothing: 'auto', + letterSpacing: 'normal', + '& .MuiDataGrid-columnsContainer': { + backgroundColor: theme.palette.mode === 'light' ? '#fafafa' : '#1d1d1d', + }, + '& .MuiDataGrid-iconSeparator': { + display: 'none', + }, + '& .MuiDataGrid-columnHeader, .MuiDataGrid-cell': { + borderRight: `1px solid ${ + theme.palette.mode === 'light' ? '#f0f0f0' : '#303030' + }`, + }, + '& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell': { + borderBottom: `1px solid ${ + theme.palette.mode === 'light' ? '#f0f0f0' : '#303030' + }`, + }, + '& .MuiDataGrid-cell': { + color: + theme.palette.mode === 'light' ? 'rgba(0,0,0,.85)' : 'rgba(255,255,255,0.85)', + fontFamily: [ + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', + 'Roboto', + '"Helvetica Neue"', + 'Arial', + 'sans-serif', + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"', + ].join(','), + WebkitFontSmoothing: 'auto', + letterSpacing: 'normal', + '& .MuiDataGrid-columnsContainer': { + backgroundColor: theme.palette.mode === 'light' ? '#fafafa' : '#1d1d1d', + }, + '& .MuiDataGrid-iconSeparator': { + display: 'none', + }, + '& .MuiDataGrid-colCell, .MuiDataGrid-cell': { + borderRight: `1px solid ${ + theme.palette.mode === 'light' ? '#f0f0f0' : '#303030' + }`, + }, + '& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell': { + borderBottom: `1px solid ${ + theme.palette.mode === 'light' ? '#f0f0f0' : '#303030' + }`, + }, + '& .MuiDataGrid-cell': { + color: + theme.palette.mode === 'light' + ? 'rgba(0,0,0,.85)' + : 'rgba(255,255,255,0.65)', + }, + '& .MuiPaginationItem-root': { + borderRadius: 0, + }, + '& .MuiCheckbox-root svg': { + width: 16, + height: 16, + backgroundColor: 'transparent', + border: `1px solid ${ + theme.palette.mode === 'light' ? '#d9d9d9' : 'rgb(67, 67, 67)' + }`, + borderRadius: 2, + }, + '& .MuiCheckbox-root svg path': { + display: 'none', + }, + '& .MuiCheckbox-root.Mui-checked:not(.MuiCheckbox-indeterminate) svg': { + backgroundColor: '#1890ff', + borderColor: '#1890ff', + }, + '& .MuiCheckbox-root.Mui-checked .MuiIconButton-label:after': { + position: 'absolute', + display: 'table', + border: '2px solid #fff', + borderTop: 0, + borderLeft: 0, + transform: 'rotate(45deg) translate(-50%,-50%)', + opacity: 1, + transition: 'all .2s cubic-bezier(.12,.4,.29,1.46) .1s', + content: '""', + top: '50%', + left: '39%', + width: 5.71428571, + height: 9.14285714, + }, + '& .MuiCheckbox-root.MuiCheckbox-indeterminate .MuiIconButton-label:after': { + width: 8, + height: 8, + backgroundColor: '#1890ff', + transform: 'none', + top: '39%', + border: 0, + }, + }, +})); + +const StyledBox = styled(Box)(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + height: 600, + width: '100%', + '& .MuiFormGroup-options': { + alignItems: 'center', + paddingBottom: theme.spacing(1), + '& > div': { + minWidth: 100, + margin: theme.spacing(2), + marginLeft: 0, + }, + }, +})); + +type GridDataType = 'Employee' | 'Commodity'; +type GridDataThemeOption = 'default' | 'ant'; + +interface GridPaginationSettings { + pagination: boolean; + autoPageSize: boolean; + pageSize: number | undefined; +} + +interface GridConfigOptions { + size: number; + type: GridDataType; + pagesize: number; + theme: GridDataThemeOption; +} + +interface GridToolbarContainerProps { + onApply: (options: GridConfigOptions) => void; + size: number; + type: GridDataType; + theme: GridDataThemeOption; +} + +function SettingsPanel(props: GridToolbarContainerProps) { + const { onApply, type, size, theme } = props; + const [sizeState, setSize] = React.useState(size); + const [typeState, setType] = React.useState(type); + const [selectedPaginationValue, setSelectedPaginationValue] = + React.useState(-1); + const [activeTheme, setActiveTheme] = React.useState(theme); + + const handleSizeChange = React.useCallback((event) => { + setSize(Number(event.target.value)); + }, []); + + const handleDatasetChange = React.useCallback((event) => { + setType(event.target.value); + }, []); + + const handlePaginationChange = React.useCallback((event) => { + setSelectedPaginationValue(event.target.value); + }, []); + + const handleThemeChange = React.useCallback((event) => { + setActiveTheme(event.target.value); + }, []); + + const handleApplyChanges = React.useCallback(() => { + onApply({ + size: sizeState, + type: typeState, + pagesize: selectedPaginationValue, + theme: activeTheme, + }); + }, [sizeState, typeState, selectedPaginationValue, activeTheme, onApply]); + + return ( + + + Dataset + + + + Rows + + + + Page Size + + + + Theme + + + + + ); +} + +export default function FullFeaturedDemo() { + const [isAntDesign, setIsAntDesign] = React.useState(false); + const [type, setType] = React.useState('Commodity'); + const [size, setSize] = React.useState(100); + const { loading, data, setRowLength, loadNewData } = useDemoData({ + dataSet: type, + rowLength: size, + maxColumns: 40, + editable: true, + }); + + const [pagination, setPagination] = React.useState({ + pagination: false, + autoPageSize: false, + pageSize: undefined, + }); + + const getActiveTheme = () => { + return isAntDesign ? 'ant' : 'default'; + }; + + const handleApplyClick: GridToolbarContainerProps['onApply'] = (settings) => { + if (size !== settings.size) { + setSize(settings.size); + } + + if (type !== settings.type) { + setType(settings.type); + } + + if (getActiveTheme() !== settings.theme) { + setIsAntDesign(!isAntDesign); + } + + if (size !== settings.size || type !== settings.type) { + setRowLength(settings.size); + loadNewData(); + } + + const newPaginationSettings: GridPaginationSettings = { + pagination: settings.pagesize !== -1, + autoPageSize: settings.pagesize === 0, + pageSize: settings.pagesize > 0 ? settings.pagesize : undefined, + }; + + setPagination( + ( + currentPaginationSettings: GridPaginationSettings, + ): GridPaginationSettings => { + if ( + currentPaginationSettings.pagination === + newPaginationSettings.pagination && + currentPaginationSettings.autoPageSize === + newPaginationSettings.autoPageSize && + currentPaginationSettings.pageSize === newPaginationSettings.pageSize + ) { + return currentPaginationSettings; + } + return newPaginationSettings; + }, + ); + }; + + const DataGridComponent = isAntDesign ? AntDesignStyledDataGridPro : DataGridPro; + + return ( + + + + + ); +} diff --git a/docs/data/data-grid/demo/demo.md b/docs/data/data-grid/demo/demo.md new file mode 100644 index 00000000000..3b226bd583f --- /dev/null +++ b/docs/data/data-grid/demo/demo.md @@ -0,0 +1,14 @@ +--- +title: Data Grid - Demo +--- + +# Data Grid - Demo + +

Use the demo below to explore the available features.

+ +{{"demo": "FullFeaturedDemo.js", "defaultCodeOpen": false, "bg": "inline"}} + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/editing-legacy/BasicEditingGrid.js b/docs/data/data-grid/editing-legacy/BasicEditingGrid.js new file mode 100644 index 00000000000..7b0773375cd --- /dev/null +++ b/docs/data/data-grid/editing-legacy/BasicEditingGrid.js @@ -0,0 +1,72 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function BasicEditingGrid() { + return ( +
+ +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/BasicEditingGrid.tsx b/docs/data/data-grid/editing-legacy/BasicEditingGrid.tsx new file mode 100644 index 00000000000..f26a9229372 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/BasicEditingGrid.tsx @@ -0,0 +1,72 @@ +import * as React from 'react'; +import { DataGrid, GridColumns, GridRowsProp } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function BasicEditingGrid() { + return ( +
+ +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/BasicEditingGrid.tsx.preview b/docs/data/data-grid/editing-legacy/BasicEditingGrid.tsx.preview new file mode 100644 index 00000000000..074afd47c44 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/BasicEditingGrid.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/editing-legacy/BasicRowEditingGrid.js b/docs/data/data-grid/editing-legacy/BasicRowEditingGrid.js new file mode 100644 index 00000000000..117125cf6f8 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/BasicRowEditingGrid.js @@ -0,0 +1,72 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function BasicRowEditingGrid() { + return ( +
+ +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/BasicRowEditingGrid.tsx b/docs/data/data-grid/editing-legacy/BasicRowEditingGrid.tsx new file mode 100644 index 00000000000..3f997b6a833 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/BasicRowEditingGrid.tsx @@ -0,0 +1,72 @@ +import * as React from 'react'; +import { DataGrid, GridColumns, GridRowsProp } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function BasicRowEditingGrid() { + return ( +
+ +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/BasicRowEditingGrid.tsx.preview b/docs/data/data-grid/editing-legacy/BasicRowEditingGrid.tsx.preview new file mode 100644 index 00000000000..dc8505213ee --- /dev/null +++ b/docs/data/data-grid/editing-legacy/BasicRowEditingGrid.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/editing-legacy/CatchEditingEventsGrid.js b/docs/data/data-grid/editing-legacy/CatchEditingEventsGrid.js new file mode 100644 index 00000000000..12943fac7d5 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/CatchEditingEventsGrid.js @@ -0,0 +1,69 @@ +import * as React from 'react'; +import Alert from '@mui/material/Alert'; +import { useGridApiRef, DataGridPro } from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function CatchEditingEventsGrid() { + const apiRef = useGridApiRef(); + const [message, setMessage] = React.useState(''); + + React.useEffect(() => { + return apiRef.current.subscribeEvent('cellEditStart', (params, event) => { + setMessage( + `Editing cell with value: ${params.value} and row id: ${params.id}, column: ${params.field}, triggered by ${event.type}.`, + ); + }); + }, [apiRef]); + + React.useEffect(() => { + return apiRef.current.subscribeEvent('cellEditStop', () => { + setMessage(''); + }); + }, [apiRef]); + + return ( +
+
+ +
+ {message && ( + + {message} + + )} +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/CatchEditingEventsGrid.tsx b/docs/data/data-grid/editing-legacy/CatchEditingEventsGrid.tsx new file mode 100644 index 00000000000..5ab8d50b808 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/CatchEditingEventsGrid.tsx @@ -0,0 +1,78 @@ +import * as React from 'react'; +import Alert from '@mui/material/Alert'; +import { + GridColumns, + GridRowsProp, + useGridApiRef, + DataGridPro, +} from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function CatchEditingEventsGrid() { + const apiRef = useGridApiRef(); + const [message, setMessage] = React.useState(''); + + React.useEffect(() => { + return apiRef.current.subscribeEvent('cellEditStart', (params, event) => { + setMessage( + `Editing cell with value: ${params.value} and row id: ${ + params.id + }, column: ${params.field}, triggered by ${ + (event as React.SyntheticEvent)!.type + }.`, + ); + }); + }, [apiRef]); + + React.useEffect(() => { + return apiRef.current.subscribeEvent('cellEditStop', () => { + setMessage(''); + }); + }, [apiRef]); + + return ( +
+
+ +
+ {message && ( + + {message} + + )} +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/CatchEditingEventsGrid.tsx.preview b/docs/data/data-grid/editing-legacy/CatchEditingEventsGrid.tsx.preview new file mode 100644 index 00000000000..1751683bd86 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/CatchEditingEventsGrid.tsx.preview @@ -0,0 +1,8 @@ +
+ +
+{message && ( + + {message} + +)} \ No newline at end of file diff --git a/docs/data/data-grid/editing-legacy/CellEditControlGrid.js b/docs/data/data-grid/editing-legacy/CellEditControlGrid.js new file mode 100644 index 00000000000..2c685b35e7b --- /dev/null +++ b/docs/data/data-grid/editing-legacy/CellEditControlGrid.js @@ -0,0 +1,89 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; +import Alert from '@mui/material/Alert'; + +export default function CellEditControlGrid() { + const [editRowsModel, setEditRowsModel] = React.useState({}); + + const handleEditRowsModelChange = React.useCallback((model) => { + setEditRowsModel(model); + }, []); + + return ( +
+ + editRowsModel: {JSON.stringify(editRowsModel)} + +
+ +
+
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/CellEditControlGrid.tsx b/docs/data/data-grid/editing-legacy/CellEditControlGrid.tsx new file mode 100644 index 00000000000..3189b033fd8 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/CellEditControlGrid.tsx @@ -0,0 +1,94 @@ +import * as React from 'react'; +import { + DataGrid, + GridColumns, + GridEditRowsModel, + GridRowsProp, +} from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; +import Alert from '@mui/material/Alert'; + +export default function CellEditControlGrid() { + const [editRowsModel, setEditRowsModel] = React.useState({}); + + const handleEditRowsModelChange = React.useCallback((model: GridEditRowsModel) => { + setEditRowsModel(model); + }, []); + + return ( +
+ + editRowsModel: {JSON.stringify(editRowsModel)} + +
+ +
+
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/CellEditControlGrid.tsx.preview b/docs/data/data-grid/editing-legacy/CellEditControlGrid.tsx.preview new file mode 100644 index 00000000000..dae480707d3 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/CellEditControlGrid.tsx.preview @@ -0,0 +1,11 @@ + + editRowsModel: {JSON.stringify(editRowsModel)} + +
+ +
\ No newline at end of file diff --git a/docs/data/data-grid/editing-legacy/CellEditServerSidePersistence.js b/docs/data/data-grid/editing-legacy/CellEditServerSidePersistence.js new file mode 100644 index 00000000000..19683c5bfbe --- /dev/null +++ b/docs/data/data-grid/editing-legacy/CellEditServerSidePersistence.js @@ -0,0 +1,124 @@ +import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; +import Snackbar from '@mui/material/Snackbar'; +import Alert from '@mui/material/Alert'; + +const useFakeMutation = () => { + return React.useCallback( + (user) => + new Promise((resolve) => + setTimeout(() => { + resolve(user); + }, 200), + ), + [], + ); +}; + +export default function CellEditServerSidePersistence() { + const mutateRow = useFakeMutation(); + const [rows, setRows] = React.useState(INITIAL_ROWS); + + const [snackbar, setSnackbar] = React.useState(null); + + const handleCloseSnackbar = () => setSnackbar(null); + + const handleCellEditCommit = React.useCallback( + async (params) => { + try { + // Make the HTTP request to save in the backend + const response = await mutateRow({ + id: params.id, + [params.field]: params.value, + }); + + setSnackbar({ children: 'User successfully saved', severity: 'success' }); + setRows((prev) => + prev.map((row) => (row.id === params.id ? { ...row, ...response } : row)), + ); + } catch (error) { + setSnackbar({ children: 'Error while saving user', severity: 'error' }); + // Restore the row in case of error + setRows((prev) => [...prev]); + } + }, + [mutateRow], + ); + + return ( +
+ + {!!snackbar && ( + + + + )} +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const INITIAL_ROWS = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/CellEditServerSidePersistence.tsx b/docs/data/data-grid/editing-legacy/CellEditServerSidePersistence.tsx new file mode 100644 index 00000000000..37f53938877 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/CellEditServerSidePersistence.tsx @@ -0,0 +1,140 @@ +import * as React from 'react'; +import { + DataGridPro, + GridCellEditCommitParams, + GridColumns, + GridRowId, + GridRowsProp, +} from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; +import Snackbar from '@mui/material/Snackbar'; +import Alert, { AlertProps } from '@mui/material/Alert'; + +interface User { + name: string; + age: number; + id: GridRowId; + dateCreated: Date; + lastLogin: Date; +} + +const useFakeMutation = () => { + return React.useCallback( + (user: Partial) => + new Promise>((resolve) => + setTimeout(() => { + resolve(user); + }, 200), + ), + [], + ); +}; + +export default function CellEditServerSidePersistence() { + const mutateRow = useFakeMutation(); + const [rows, setRows] = React.useState(INITIAL_ROWS); + + const [snackbar, setSnackbar] = React.useState | null>(null); + + const handleCloseSnackbar = () => setSnackbar(null); + + const handleCellEditCommit = React.useCallback( + async (params: GridCellEditCommitParams) => { + try { + // Make the HTTP request to save in the backend + const response = await mutateRow({ + id: params.id, + [params.field]: params.value, + }); + setSnackbar({ children: 'User successfully saved', severity: 'success' }); + setRows((prev) => + prev.map((row) => (row.id === params.id ? { ...row, ...response } : row)), + ); + } catch (error) { + setSnackbar({ children: 'Error while saving user', severity: 'error' }); + // Restore the row in case of error + setRows((prev) => [...prev]); + } + }, + [mutateRow], + ); + + return ( +
+ + {!!snackbar && ( + + + + )} +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const INITIAL_ROWS: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/CellEditServerSidePersistence.tsx.preview b/docs/data/data-grid/editing-legacy/CellEditServerSidePersistence.tsx.preview new file mode 100644 index 00000000000..69e27be53dc --- /dev/null +++ b/docs/data/data-grid/editing-legacy/CellEditServerSidePersistence.tsx.preview @@ -0,0 +1,10 @@ + +{!!snackbar && ( + + + +)} \ No newline at end of file diff --git a/docs/data/data-grid/editing-legacy/ConditionalValidationGrid.js b/docs/data/data-grid/editing-legacy/ConditionalValidationGrid.js new file mode 100644 index 00000000000..3c65cd63c74 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/ConditionalValidationGrid.js @@ -0,0 +1,97 @@ +import * as React from 'react'; +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import { DataGridPro, useGridApiRef } from '@mui/x-data-grid-pro'; +import { randomPrice } from '@mui/x-data-grid-generator'; + +const StyledBox = styled(Box)(({ theme }) => ({ + height: 400, + width: '100%', + '& .MuiDataGrid-cell--editing': { + backgroundColor: 'rgb(255,215,115, 0.19)', + color: '#1a3e72', + '& .MuiInputBase-root': { + height: '100%', + }, + }, + '& .Mui-error': { + backgroundColor: `rgb(126,10,15, ${theme.palette.mode === 'dark' ? 0 : 0.1})`, + color: theme.palette.error.main, + }, +})); + +const rows = [ + { + id: 1, + expense: 'Light bill', + price: randomPrice(0, 1000), + dueAt: new Date(2021, 6, 8), + isPaid: false, + paymentMethod: '', + }, + { + id: 2, + expense: 'Rent', + price: randomPrice(0, 1000), + dueAt: new Date(2021, 7, 1), + isPaid: false, + paymentMethod: '', + }, + { + id: 3, + expense: 'Car insurance', + price: randomPrice(0, 1000), + dueAt: new Date(2021, 7, 4), + isPaid: true, + paymentMethod: 'Wire transfer', + }, +]; + +export default function ConditionalValidationGrid() { + const apiRef = useGridApiRef(); + + const columns = [ + { field: 'expense', headerName: 'Expense', width: 160, editable: true }, + { + field: 'price', + headerName: 'Price', + type: 'number', + width: 120, + editable: true, + }, + { + field: 'dueAt', + headerName: 'Due at', + type: 'date', + width: 120, + editable: true, + }, + { + field: 'isPaid', + headerName: 'Is paid?', + type: 'boolean', + width: 140, + editable: true, + }, + { + field: 'paymentMethod', + headerName: 'Payment method', + type: 'singleSelect', + valueOptions: ['Credit card', 'Wire transfer', 'Cash'], + width: 160, + editable: true, + preProcessEditCellProps: (params) => { + const editRowsModel = apiRef.current.getEditRowsModel(); + const isPaidProps = editRowsModel[params.id].isPaid; + const hasError = isPaidProps.value && !params.props.value; + return { ...params.props, error: hasError }; + }, + }, + ]; + + return ( + + + + ); +} diff --git a/docs/data/data-grid/editing-legacy/ConditionalValidationGrid.tsx b/docs/data/data-grid/editing-legacy/ConditionalValidationGrid.tsx new file mode 100644 index 00000000000..225a2d40c6a --- /dev/null +++ b/docs/data/data-grid/editing-legacy/ConditionalValidationGrid.tsx @@ -0,0 +1,102 @@ +import * as React from 'react'; +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import { + DataGridPro, + GridColumns, + GridRowsProp, + useGridApiRef, +} from '@mui/x-data-grid-pro'; +import { randomPrice } from '@mui/x-data-grid-generator'; + +const StyledBox = styled(Box)(({ theme }) => ({ + height: 400, + width: '100%', + '& .MuiDataGrid-cell--editing': { + backgroundColor: 'rgb(255,215,115, 0.19)', + color: '#1a3e72', + '& .MuiInputBase-root': { + height: '100%', + }, + }, + '& .Mui-error': { + backgroundColor: `rgb(126,10,15, ${theme.palette.mode === 'dark' ? 0 : 0.1})`, + color: theme.palette.error.main, + }, +})); + +const rows: GridRowsProp = [ + { + id: 1, + expense: 'Light bill', + price: randomPrice(0, 1000), + dueAt: new Date(2021, 6, 8), + isPaid: false, + paymentMethod: '', + }, + { + id: 2, + expense: 'Rent', + price: randomPrice(0, 1000), + dueAt: new Date(2021, 7, 1), + isPaid: false, + paymentMethod: '', + }, + { + id: 3, + expense: 'Car insurance', + price: randomPrice(0, 1000), + dueAt: new Date(2021, 7, 4), + isPaid: true, + paymentMethod: 'Wire transfer', + }, +]; + +export default function ConditionalValidationGrid() { + const apiRef = useGridApiRef(); + + const columns: GridColumns = [ + { field: 'expense', headerName: 'Expense', width: 160, editable: true }, + { + field: 'price', + headerName: 'Price', + type: 'number', + width: 120, + editable: true, + }, + { + field: 'dueAt', + headerName: 'Due at', + type: 'date', + width: 120, + editable: true, + }, + { + field: 'isPaid', + headerName: 'Is paid?', + type: 'boolean', + width: 140, + editable: true, + }, + { + field: 'paymentMethod', + headerName: 'Payment method', + type: 'singleSelect', + valueOptions: ['Credit card', 'Wire transfer', 'Cash'], + width: 160, + editable: true, + preProcessEditCellProps: (params) => { + const editRowsModel = apiRef.current.getEditRowsModel(); + const isPaidProps = editRowsModel[params.id].isPaid; + const hasError = isPaidProps.value && !params.props.value; + return { ...params.props, error: hasError }; + }, + }, + ]; + + return ( + + + + ); +} diff --git a/docs/data/data-grid/editing-legacy/ConditionalValidationGrid.tsx.preview b/docs/data/data-grid/editing-legacy/ConditionalValidationGrid.tsx.preview new file mode 100644 index 00000000000..4b9f3e5844b --- /dev/null +++ b/docs/data/data-grid/editing-legacy/ConditionalValidationGrid.tsx.preview @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/docs/data/data-grid/editing-legacy/EditApiNoSnap.js b/docs/data/data-grid/editing-legacy/EditApiNoSnap.js new file mode 100644 index 00000000000..ba58d4ca509 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/EditApiNoSnap.js @@ -0,0 +1,7 @@ +import React from 'react'; +import ApiDocs from 'docsx/src/modules/components/ApiDocs'; +import api from 'docsx/pages/x/api/data-grid/grid-old-editing-api.json'; + +export default function EditApiNoSnap() { + return ; +} diff --git a/docs/data/data-grid/editing-legacy/FullFeaturedCrudGrid.js b/docs/data/data-grid/editing-legacy/FullFeaturedCrudGrid.js new file mode 100644 index 00000000000..d125520ba88 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/FullFeaturedCrudGrid.js @@ -0,0 +1,232 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import AddIcon from '@mui/icons-material/Add'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/DeleteOutlined'; +import SaveIcon from '@mui/icons-material/Save'; +import CancelIcon from '@mui/icons-material/Close'; +import { + useGridApiRef, + DataGridPro, + GridToolbarContainer, + GridActionsCellItem, +} from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, + randomId, +} from '@mui/x-data-grid-generator'; + +const rows = [ + { + id: randomId(), + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; + +function EditToolbar(props) { + const { apiRef } = props; + + const handleClick = () => { + const id = randomId(); + apiRef.current.updateRows([{ id, isNew: true }]); + apiRef.current.setRowMode(id, 'edit'); + // Wait for the grid to render with the new row + setTimeout(() => { + apiRef.current.scrollToIndexes({ + rowIndex: apiRef.current.getRowsCount() - 1, + }); + + apiRef.current.setCellFocus(id, 'name'); + }); + }; + + return ( + + + + ); +} + +EditToolbar.propTypes = { + apiRef: PropTypes.shape({ + current: PropTypes.object.isRequired, + }).isRequired, +}; + +export default function FullFeaturedCrudGrid() { + const apiRef = useGridApiRef(); + + const handleRowEditStart = (params, event) => { + event.defaultMuiPrevented = true; + }; + + const handleRowEditStop = (params, event) => { + event.defaultMuiPrevented = true; + }; + + const handleCellFocusOut = (params, event) => { + event.defaultMuiPrevented = true; + }; + + const handleEditClick = (id) => (event) => { + event.stopPropagation(); + apiRef.current.setRowMode(id, 'edit'); + }; + + const handleSaveClick = (id) => async (event) => { + event.stopPropagation(); + // Wait for the validation to run + const isValid = await apiRef.current.commitRowChange(id); + if (isValid) { + apiRef.current.setRowMode(id, 'view'); + const row = apiRef.current.getRow(id); + apiRef.current.updateRows([{ ...row, isNew: false }]); + } + }; + + const handleDeleteClick = (id) => (event) => { + event.stopPropagation(); + apiRef.current.updateRows([{ id, _action: 'delete' }]); + }; + + const handleCancelClick = (id) => (event) => { + event.stopPropagation(); + apiRef.current.setRowMode(id, 'view'); + + const row = apiRef.current.getRow(id); + if (row.isNew) { + apiRef.current.updateRows([{ id, _action: 'delete' }]); + } + }; + + const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, + { + field: 'actions', + type: 'actions', + headerName: 'Actions', + width: 100, + cellClassName: 'actions', + getActions: ({ id }) => { + const isInEditMode = apiRef.current.getRowMode(id) === 'edit'; + + if (isInEditMode) { + return [ + } + label="Save" + onClick={handleSaveClick(id)} + color="primary" + />, + } + label="Cancel" + className="textPrimary" + onClick={handleCancelClick(id)} + color="inherit" + />, + ]; + } + + return [ + } + label="Edit" + className="textPrimary" + onClick={handleEditClick(id)} + color="inherit" + />, + } + label="Delete" + onClick={handleDeleteClick(id)} + color="inherit" + />, + ]; + }, + }, + ]; + + return ( + + + + ); +} diff --git a/docs/data/data-grid/editing-legacy/FullFeaturedCrudGrid.tsx b/docs/data/data-grid/editing-legacy/FullFeaturedCrudGrid.tsx new file mode 100644 index 00000000000..0cb79c25880 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/FullFeaturedCrudGrid.tsx @@ -0,0 +1,238 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import AddIcon from '@mui/icons-material/Add'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/DeleteOutlined'; +import SaveIcon from '@mui/icons-material/Save'; +import CancelIcon from '@mui/icons-material/Close'; +import { + GridRowsProp, + useGridApiRef, + DataGridPro, + GridApi, + GridColumns, + GridRowParams, + MuiEvent, + GridToolbarContainer, + GridActionsCellItem, + GridEventListener, + GridRowId, +} from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, + randomId, +} from '@mui/x-data-grid-generator'; + +const rows: GridRowsProp = [ + { + id: randomId(), + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; + +interface EditToolbarProps { + apiRef: React.MutableRefObject; +} + +function EditToolbar(props: EditToolbarProps) { + const { apiRef } = props; + + const handleClick = () => { + const id = randomId(); + apiRef.current.updateRows([{ id, isNew: true }]); + apiRef.current.setRowMode(id, 'edit'); + // Wait for the grid to render with the new row + setTimeout(() => { + apiRef.current.scrollToIndexes({ + rowIndex: apiRef.current.getRowsCount() - 1, + }); + apiRef.current.setCellFocus(id, 'name'); + }); + }; + + return ( + + + + ); +} + +export default function FullFeaturedCrudGrid() { + const apiRef = useGridApiRef(); + + const handleRowEditStart = ( + params: GridRowParams, + event: MuiEvent, + ) => { + event.defaultMuiPrevented = true; + }; + + const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => { + event.defaultMuiPrevented = true; + }; + + const handleCellFocusOut: GridEventListener<'cellFocusOut'> = (params, event) => { + event.defaultMuiPrevented = true; + }; + + const handleEditClick = (id: GridRowId) => (event: React.MouseEvent) => { + event.stopPropagation(); + apiRef.current.setRowMode(id, 'edit'); + }; + + const handleSaveClick = (id: GridRowId) => async (event: React.MouseEvent) => { + event.stopPropagation(); + // Wait for the validation to run + const isValid = await apiRef.current.commitRowChange(id); + if (isValid) { + apiRef.current.setRowMode(id, 'view'); + const row = apiRef.current.getRow(id); + apiRef.current.updateRows([{ ...row, isNew: false }]); + } + }; + + const handleDeleteClick = (id: GridRowId) => (event: React.MouseEvent) => { + event.stopPropagation(); + apiRef.current.updateRows([{ id, _action: 'delete' }]); + }; + + const handleCancelClick = (id: GridRowId) => (event: React.MouseEvent) => { + event.stopPropagation(); + apiRef.current.setRowMode(id, 'view'); + + const row = apiRef.current.getRow(id); + if (row!.isNew) { + apiRef.current.updateRows([{ id, _action: 'delete' }]); + } + }; + + const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, + { + field: 'actions', + type: 'actions', + headerName: 'Actions', + width: 100, + cellClassName: 'actions', + getActions: ({ id }) => { + const isInEditMode = apiRef.current.getRowMode(id) === 'edit'; + + if (isInEditMode) { + return [ + } + label="Save" + onClick={handleSaveClick(id)} + color="primary" + />, + } + label="Cancel" + className="textPrimary" + onClick={handleCancelClick(id)} + color="inherit" + />, + ]; + } + + return [ + } + label="Edit" + className="textPrimary" + onClick={handleEditClick(id)} + color="inherit" + />, + } + label="Delete" + onClick={handleDeleteClick(id)} + color="inherit" + />, + ]; + }, + }, + ]; + + return ( + + + + ); +} diff --git a/docs/data/data-grid/editing-legacy/FullFeaturedCrudGrid.tsx.preview b/docs/data/data-grid/editing-legacy/FullFeaturedCrudGrid.tsx.preview new file mode 100644 index 00000000000..51dcddd20e0 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/FullFeaturedCrudGrid.tsx.preview @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/editing-legacy/IsCellEditableGrid.js b/docs/data/data-grid/editing-legacy/IsCellEditableGrid.js new file mode 100644 index 00000000000..46b4787fd2a --- /dev/null +++ b/docs/data/data-grid/editing-legacy/IsCellEditableGrid.js @@ -0,0 +1,86 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function IsCellEditableGrid() { + return ( + + theme.palette.mode === 'dark' ? '#376331' : 'rgb(217 243 190)', + }, + }} + > + params.row.age % 2 === 0} + /> + + ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/IsCellEditableGrid.tsx b/docs/data/data-grid/editing-legacy/IsCellEditableGrid.tsx new file mode 100644 index 00000000000..e279e762f6c --- /dev/null +++ b/docs/data/data-grid/editing-legacy/IsCellEditableGrid.tsx @@ -0,0 +1,86 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid, GridColumns, GridRowsProp } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function IsCellEditableGrid() { + return ( + + theme.palette.mode === 'dark' ? '#376331' : 'rgb(217 243 190)', + }, + }} + > + params.row.age % 2 === 0} + /> + + ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/IsCellEditableGrid.tsx.preview b/docs/data/data-grid/editing-legacy/IsCellEditableGrid.tsx.preview new file mode 100644 index 00000000000..9df94af1795 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/IsCellEditableGrid.tsx.preview @@ -0,0 +1,5 @@ + params.row.age % 2 === 0} +/> \ No newline at end of file diff --git a/docs/data/data-grid/editing-legacy/RenderRatingEditCellGrid.js b/docs/data/data-grid/editing-legacy/RenderRatingEditCellGrid.js new file mode 100644 index 00000000000..7d60034e87a --- /dev/null +++ b/docs/data/data-grid/editing-legacy/RenderRatingEditCellGrid.js @@ -0,0 +1,110 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import Rating from '@mui/material/Rating'; +import { DataGrid } from '@mui/x-data-grid'; + +function renderRating(params) { + return ; +} + +renderRating.propTypes = { + /** + * The cell value, but if the column has valueGetter, use getValue. + */ + value: PropTypes.number, +}; + +function RatingEditInputCell(props) { + const { id, value, api, field } = props; + + const handleChange = async (event, newValue) => { + api.setEditCellValue({ id, field, value: Number(newValue) }, event); + // Check if the event is not from the keyboard + // https://github.com/facebook/react/issues/7407 + const nativeEvent = event.nativeEvent; + if (nativeEvent.clientX !== 0 && nativeEvent.clientY !== 0) { + // Wait for the validation to run + const isValid = await api.commitCellChange({ id, field }); + if (isValid) { + api.setCellMode(id, field, 'view'); + } + } + }; + + const handleRef = (element) => { + if (element) { + const input = element.querySelector(`input[value="${value}"]`); + + input?.focus(); + } + }; + + return ( + + + + ); +} + +RatingEditInputCell.propTypes = { + /** + * GridApi that let you manipulate the grid. + * @deprecated Use the `apiRef` returned by `useGridApiContext` or `useGridApiRef` (only available in `@mui/x-data-grid-pro`) + */ + api: PropTypes.any.isRequired, + /** + * The column field of the cell that triggered the event. + */ + field: PropTypes.string.isRequired, + /** + * The grid row id. + */ + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + /** + * The cell value, but if the column has valueGetter, use getValue. + */ + value: PropTypes.number, +}; + +const renderRatingEditInputCell = (params) => { + return ; +}; + +export default function RenderRatingEditCellGrid() { + return ( +
+ +
+ ); +} + +const columns = [ + { + field: 'places', + headerName: 'Places', + width: 120, + }, + { + field: 'rating', + headerName: 'Rating', + renderCell: renderRating, + renderEditCell: renderRatingEditInputCell, + editable: true, + width: 180, + type: 'number', + }, +]; + +const rows = [ + { id: 1, places: 'Barcelona', rating: 5 }, + { id: 2, places: 'Rio de Janeiro', rating: 4 }, + { id: 3, places: 'London', rating: 3 }, + { id: 4, places: 'New York', rating: 2 }, +]; diff --git a/docs/data/data-grid/editing-legacy/RenderRatingEditCellGrid.tsx b/docs/data/data-grid/editing-legacy/RenderRatingEditCellGrid.tsx new file mode 100644 index 00000000000..44e69b9db9f --- /dev/null +++ b/docs/data/data-grid/editing-legacy/RenderRatingEditCellGrid.tsx @@ -0,0 +1,83 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Rating, { RatingProps } from '@mui/material/Rating'; +import { DataGrid, GridRenderCellParams, GridColDef } from '@mui/x-data-grid'; + +function renderRating(params: GridRenderCellParams) { + return ; +} + +function RatingEditInputCell(props: GridRenderCellParams) { + const { id, value, api, field } = props; + + const handleChange: RatingProps['onChange'] = async (event, newValue) => { + api.setEditCellValue({ id, field, value: Number(newValue) }, event); + // Check if the event is not from the keyboard + // https://github.com/facebook/react/issues/7407 + const nativeEvent = event.nativeEvent as unknown as MouseEvent; + if (nativeEvent.clientX !== 0 && nativeEvent.clientY !== 0) { + // Wait for the validation to run + const isValid = await api.commitCellChange({ id, field }); + if (isValid) { + api.setCellMode(id, field, 'view'); + } + } + }; + + const handleRef = (element: HTMLElement) => { + if (element) { + const input = element.querySelector( + `input[value="${value}"]`, + ) as HTMLInputElement; + input?.focus(); + } + }; + + return ( + + + + ); +} + +const renderRatingEditInputCell: GridColDef['renderEditCell'] = (params) => { + return ; +}; + +export default function RenderRatingEditCellGrid() { + return ( +
+ +
+ ); +} + +const columns = [ + { + field: 'places', + headerName: 'Places', + width: 120, + }, + { + field: 'rating', + headerName: 'Rating', + renderCell: renderRating, + renderEditCell: renderRatingEditInputCell, + editable: true, + width: 180, + type: 'number', + }, +]; + +const rows = [ + { id: 1, places: 'Barcelona', rating: 5 }, + { id: 2, places: 'Rio de Janeiro', rating: 4 }, + { id: 3, places: 'London', rating: 3 }, + { id: 4, places: 'New York', rating: 2 }, +]; diff --git a/docs/data/data-grid/editing-legacy/RenderRatingEditCellGrid.tsx.preview b/docs/data/data-grid/editing-legacy/RenderRatingEditCellGrid.tsx.preview new file mode 100644 index 00000000000..074afd47c44 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/RenderRatingEditCellGrid.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/editing-legacy/RowEditControlGrid.js b/docs/data/data-grid/editing-legacy/RowEditControlGrid.js new file mode 100644 index 00000000000..f8293a442a9 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/RowEditControlGrid.js @@ -0,0 +1,90 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; +import Alert from '@mui/material/Alert'; + +export default function RowEditControlGrid() { + const [editRowsModel, setEditRowsModel] = React.useState({}); + + const handleEditRowsModelChange = React.useCallback((model) => { + setEditRowsModel(model); + }, []); + + return ( +
+
+ +
+ + editRowsModel: {JSON.stringify(editRowsModel)} + +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/RowEditControlGrid.tsx b/docs/data/data-grid/editing-legacy/RowEditControlGrid.tsx new file mode 100644 index 00000000000..146364c9455 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/RowEditControlGrid.tsx @@ -0,0 +1,95 @@ +import * as React from 'react'; +import { + DataGrid, + GridColumns, + GridEditRowsModel, + GridRowsProp, +} from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; +import Alert from '@mui/material/Alert'; + +export default function RowEditControlGrid() { + const [editRowsModel, setEditRowsModel] = React.useState({}); + + const handleEditRowsModelChange = React.useCallback((model: GridEditRowsModel) => { + setEditRowsModel(model); + }, []); + + return ( +
+
+ +
+ + editRowsModel: {JSON.stringify(editRowsModel)} + +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/RowEditControlGrid.tsx.preview b/docs/data/data-grid/editing-legacy/RowEditControlGrid.tsx.preview new file mode 100644 index 00000000000..6d3d9737d30 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/RowEditControlGrid.tsx.preview @@ -0,0 +1,12 @@ +
+ +
+ + editRowsModel: {JSON.stringify(editRowsModel)} + \ No newline at end of file diff --git a/docs/data/data-grid/editing-legacy/RowEditServerSidePersistence.js b/docs/data/data-grid/editing-legacy/RowEditServerSidePersistence.js new file mode 100644 index 00000000000..51a5ea9838f --- /dev/null +++ b/docs/data/data-grid/editing-legacy/RowEditServerSidePersistence.js @@ -0,0 +1,130 @@ +import * as React from 'react'; +import { DataGridPro, useGridApiRef } from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; +import Snackbar from '@mui/material/Snackbar'; +import Alert from '@mui/material/Alert'; + +const useFakeMutation = () => { + return React.useCallback( + (user) => + new Promise((resolve) => + setTimeout(() => { + resolve(user); + }, 200), + ), + [], + ); +}; + +export default function RowEditServerSidePersistence() { + const mutateRow = useFakeMutation(); + const apiRef = useGridApiRef(); + const [snackbar, setSnackbar] = React.useState(null); + + const handleCloseSnackbar = () => setSnackbar(null); + + const handleRowEditCommit = React.useCallback( + async (id) => { + const model = apiRef.current.getEditRowsModel(); // This object contains all rows that are being edited + const newRow = model[id]; // The data that will be committed + + // The new value entered + const name = newRow.name.value; + const age = newRow.age.value; + const lastLogin = newRow.lastLogin.value; + const dateCreated = newRow.dateCreated.value; + + // Get the row old value before committing + const oldRow = apiRef.current.getRow(id); + + try { + // Make the HTTP request to save in the backend + await mutateRow({ id, name, age, lastLogin, dateCreated }); + setSnackbar({ children: 'User successfully saved', severity: 'success' }); + } catch (error) { + setSnackbar({ children: 'Error while saving user', severity: 'error' }); + // Restore the row in case of error + apiRef.current.updateRows([oldRow]); + } + }, + [apiRef, mutateRow], + ); + + return ( +
+ + {!!snackbar && ( + + + + )} +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/RowEditServerSidePersistence.tsx b/docs/data/data-grid/editing-legacy/RowEditServerSidePersistence.tsx new file mode 100644 index 00000000000..c5e09470d5d --- /dev/null +++ b/docs/data/data-grid/editing-legacy/RowEditServerSidePersistence.tsx @@ -0,0 +1,147 @@ +import * as React from 'react'; +import { + DataGridPro, + GridColumns, + GridRowId, + GridRowsProp, + useGridApiRef, +} from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; +import Snackbar from '@mui/material/Snackbar'; +import Alert, { AlertProps } from '@mui/material/Alert'; + +interface User { + name: string; + age: number; + id: GridRowId; + dateCreated: Date; + lastLogin: Date; +} + +const useFakeMutation = () => { + return React.useCallback( + (user: User) => + new Promise((resolve) => + setTimeout(() => { + resolve(user); + }, 200), + ), + [], + ); +}; + +export default function RowEditServerSidePersistence() { + const mutateRow = useFakeMutation(); + const apiRef = useGridApiRef(); + const [snackbar, setSnackbar] = React.useState | null>(null); + + const handleCloseSnackbar = () => setSnackbar(null); + + const handleRowEditCommit = React.useCallback( + async (id: GridRowId) => { + const model = apiRef.current.getEditRowsModel(); // This object contains all rows that are being edited + const newRow = model[id]; // The data that will be committed + + // The new value entered + const name = newRow.name.value as string; + const age = newRow.age.value as number; + const lastLogin = newRow.lastLogin.value as Date; + const dateCreated = newRow.dateCreated.value as Date; + + // Get the row old value before committing + const oldRow = apiRef.current.getRow(id)!; + + try { + // Make the HTTP request to save in the backend + await mutateRow({ id, name, age, lastLogin, dateCreated }); + setSnackbar({ children: 'User successfully saved', severity: 'success' }); + } catch (error) { + setSnackbar({ children: 'Error while saving user', severity: 'error' }); + // Restore the row in case of error + apiRef.current.updateRows([oldRow]); + } + }, + [apiRef, mutateRow], + ); + + return ( +
+ + {!!snackbar && ( + + + + )} +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/RowEditServerSidePersistence.tsx.preview b/docs/data/data-grid/editing-legacy/RowEditServerSidePersistence.tsx.preview new file mode 100644 index 00000000000..6cf2b2c3e95 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/RowEditServerSidePersistence.tsx.preview @@ -0,0 +1,12 @@ + +{!!snackbar && ( + + + +)} \ No newline at end of file diff --git a/docs/data/data-grid/editing-legacy/StartEditButtonGrid.js b/docs/data/data-grid/editing-legacy/StartEditButtonGrid.js new file mode 100644 index 00000000000..0e9f2e3c269 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/StartEditButtonGrid.js @@ -0,0 +1,173 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import { useGridApiRef, DataGridPro } from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +function EditToolbar(props) { + const { selectedCellParams, apiRef, setSelectedCellParams } = props; + + const handleClick = async () => { + if (!selectedCellParams) { + return; + } + const { id, field, cellMode } = selectedCellParams; + if (cellMode === 'edit') { + // Wait for the validation to run + const isValid = await apiRef.current.commitCellChange({ id, field }); + if (isValid) { + apiRef.current.setCellMode(id, field, 'view'); + setSelectedCellParams({ ...selectedCellParams, cellMode: 'view' }); + } + } else { + apiRef.current.setCellMode(id, field, 'edit'); + setSelectedCellParams({ ...selectedCellParams, cellMode: 'edit' }); + } + }; + + const handleMouseDown = (event) => { + // Keep the focus in the cell + event.preventDefault(); + }; + + return ( + + + + ); +} + +EditToolbar.propTypes = { + apiRef: PropTypes.shape({ + current: PropTypes.object.isRequired, + }).isRequired, + selectedCellParams: PropTypes.any, + setSelectedCellParams: PropTypes.func.isRequired, +}; + +export default function StartEditButtonGrid() { + const apiRef = useGridApiRef(); + const [selectedCellParams, setSelectedCellParams] = React.useState(null); + + const handleCellClick = React.useCallback((params) => { + setSelectedCellParams(params); + }, []); + + const handleDoubleCellClick = React.useCallback((params, event) => { + event.defaultMuiPrevented = true; + }, []); + + // Prevent from rolling back on escape + const handleCellKeyDown = React.useCallback((params, event) => { + if (['Escape', 'Delete', 'Backspace', 'Enter'].includes(event.key)) { + event.defaultMuiPrevented = true; + } + }, []); + + // Prevent from committing on focus out + const handleCellFocusOut = React.useCallback((params, event) => { + if (params.cellMode === 'edit' && event) { + event.defaultMuiPrevented = true; + } + }, []); + + return ( +
+ +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/StartEditButtonGrid.tsx b/docs/data/data-grid/editing-legacy/StartEditButtonGrid.tsx new file mode 100644 index 00000000000..07d74f1d4a8 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/StartEditButtonGrid.tsx @@ -0,0 +1,182 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import { + GridColumns, + GridRowsProp, + useGridApiRef, + DataGridPro, + GridCellParams, + GridApi, +} from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +interface EditToolbarProps { + apiRef: React.MutableRefObject; + selectedCellParams?: any; + setSelectedCellParams: (value: any) => void; +} + +function EditToolbar(props: EditToolbarProps) { + const { selectedCellParams, apiRef, setSelectedCellParams } = props; + + const handleClick = async () => { + if (!selectedCellParams) { + return; + } + const { id, field, cellMode } = selectedCellParams; + if (cellMode === 'edit') { + // Wait for the validation to run + const isValid = await apiRef.current.commitCellChange({ id, field }); + if (isValid) { + apiRef.current.setCellMode(id, field, 'view'); + setSelectedCellParams({ ...selectedCellParams, cellMode: 'view' }); + } + } else { + apiRef.current.setCellMode(id, field, 'edit'); + setSelectedCellParams({ ...selectedCellParams, cellMode: 'edit' }); + } + }; + + const handleMouseDown = (event: React.MouseEvent) => { + // Keep the focus in the cell + event.preventDefault(); + }; + + return ( + + + + ); +} + +export default function StartEditButtonGrid() { + const apiRef = useGridApiRef(); + const [selectedCellParams, setSelectedCellParams] = + React.useState(null); + + const handleCellClick = React.useCallback((params: GridCellParams) => { + setSelectedCellParams(params); + }, []); + + const handleDoubleCellClick = React.useCallback((params, event) => { + event.defaultMuiPrevented = true; + }, []); + + // Prevent from rolling back on escape + const handleCellKeyDown = React.useCallback((params, event) => { + if ( + ['Escape', 'Delete', 'Backspace', 'Enter'].includes( + (event as React.KeyboardEvent).key, + ) + ) { + event.defaultMuiPrevented = true; + } + }, []); + + // Prevent from committing on focus out + const handleCellFocusOut = React.useCallback((params, event) => { + if (params.cellMode === 'edit' && event) { + event.defaultMuiPrevented = true; + } + }, []); + + return ( +
+ +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing-legacy/ValidateRowModelControlGrid.js b/docs/data/data-grid/editing-legacy/ValidateRowModelControlGrid.js new file mode 100644 index 00000000000..f2a7df81e62 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/ValidateRowModelControlGrid.js @@ -0,0 +1,71 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'email', 'dateCreated', 'lastUpdated']; + +function validateEmail(email) { + const re = + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(String(email).toLowerCase()); +} + +export default function ValidateRowModelControlGrid() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 5, + }); + + const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { + field: 'email', + headerName: 'Email', + width: 200, + editable: true, + preProcessEditCellProps: (params) => { + const isValid = validateEmail(params.props.value); + return { ...params.props, error: !isValid }; + }, + }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastUpdated', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, + ]; + + return ( + + `rgb(126,10,15, ${theme.palette.mode === 'dark' ? 0 : 0.1})`, + color: (theme) => (theme.palette.mode === 'dark' ? '#ff4343' : '#750f0f'), + }, + }} + > + + + ); +} diff --git a/docs/data/data-grid/editing-legacy/ValidateRowModelControlGrid.tsx b/docs/data/data-grid/editing-legacy/ValidateRowModelControlGrid.tsx new file mode 100644 index 00000000000..4ad5dbfd456 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/ValidateRowModelControlGrid.tsx @@ -0,0 +1,74 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { + DataGrid, + GridColumns, + GridPreProcessEditCellProps, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'email', 'dateCreated', 'lastUpdated']; + +function validateEmail(email: string) { + const re = + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(String(email).toLowerCase()); +} + +export default function ValidateRowModelControlGrid() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 5, + }); + const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { + field: 'email', + headerName: 'Email', + width: 200, + editable: true, + preProcessEditCellProps: (params: GridPreProcessEditCellProps) => { + const isValid = validateEmail(params.props.value); + return { ...params.props, error: !isValid }; + }, + }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastUpdated', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, + ]; + + return ( + + `rgb(126,10,15, ${theme.palette.mode === 'dark' ? 0 : 0.1})`, + color: (theme) => (theme.palette.mode === 'dark' ? '#ff4343' : '#750f0f'), + }, + }} + > + + + ); +} diff --git a/docs/data/data-grid/editing-legacy/ValidateRowModelControlGrid.tsx.preview b/docs/data/data-grid/editing-legacy/ValidateRowModelControlGrid.tsx.preview new file mode 100644 index 00000000000..50c4ffdb6a3 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/ValidateRowModelControlGrid.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/editing-legacy/ValidateServerNameGrid.js b/docs/data/data-grid/editing-legacy/ValidateServerNameGrid.js new file mode 100644 index 00000000000..7573bb1b9fe --- /dev/null +++ b/docs/data/data-grid/editing-legacy/ValidateServerNameGrid.js @@ -0,0 +1,100 @@ +import * as React from 'react'; +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import { useGridApiRef, DataGridPro } from '@mui/x-data-grid-pro'; + +const StyledBox = styled(Box)(({ theme }) => ({ + height: 400, + width: '100%', + '& .MuiDataGrid-cell--editable': { + backgroundColor: theme.palette.mode === 'dark' ? '#376331' : 'rgb(217 243 190)', + '& .MuiInputBase-root': { + height: '100%', + }, + }, + '& .Mui-error': { + backgroundColor: `rgb(126,10,15, ${theme.palette.mode === 'dark' ? 0 : 0.1})`, + color: theme.palette.mode === 'dark' ? '#ff4343' : '#750f0f', + }, +})); + +let promiseTimeout; +function validateName(username) { + const existingUsers = rows.map((row) => row.name.toLowerCase()); + + return new Promise((resolve) => { + promiseTimeout = setTimeout(() => { + resolve(existingUsers.indexOf(username.toLowerCase()) === -1); + }, Math.random() * 500 + 100); // simulate network latency + }); +} + +export default function ValidateServerNameGrid() { + const apiRef = useGridApiRef(); + + const keyStrokeTimeoutRef = React.useRef(); + + const preProcessEditCellProps = (params) => + new Promise((resolve) => { + clearTimeout(promiseTimeout); + clearTimeout(keyStrokeTimeoutRef.current); + + // basic debouncing here + keyStrokeTimeoutRef.current = setTimeout(async () => { + const isValid = await validateName(params.props.value.toString()); + resolve({ ...params.props, error: !isValid }); + }, 100); + }); + + const columns = [ + { + field: 'name', + headerName: 'MUI Contributor', + width: 180, + editable: true, + preProcessEditCellProps, + }, + ]; + + React.useEffect(() => { + return () => { + clearTimeout(promiseTimeout); + clearTimeout(keyStrokeTimeoutRef.current); + }; + }, []); + + return ( + + params.row.id === 5} + experimentalFeatures={{ preventCommitWhileValidating: true }} // Prevents a 2nd call to preProcessEditCellProps while waiting for the 1st + /> + + ); +} + +const rows = [ + { + id: 1, + name: 'Damien', + }, + { + id: 2, + name: 'Olivier', + }, + { + id: 3, + name: 'Danail', + }, + { + id: 4, + name: 'Matheus', + }, + { + id: 5, + name: 'You?', + }, +]; diff --git a/docs/data/data-grid/editing-legacy/ValidateServerNameGrid.tsx b/docs/data/data-grid/editing-legacy/ValidateServerNameGrid.tsx new file mode 100644 index 00000000000..23a41372f34 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/ValidateServerNameGrid.tsx @@ -0,0 +1,107 @@ +import * as React from 'react'; +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import { + GridColumns, + GridRowsProp, + useGridApiRef, + DataGridPro, + GridEditCellProps, + GridPreProcessEditCellProps, +} from '@mui/x-data-grid-pro'; + +const StyledBox = styled(Box)(({ theme }) => ({ + height: 400, + width: '100%', + '& .MuiDataGrid-cell--editable': { + backgroundColor: theme.palette.mode === 'dark' ? '#376331' : 'rgb(217 243 190)', + '& .MuiInputBase-root': { + height: '100%', + }, + }, + '& .Mui-error': { + backgroundColor: `rgb(126,10,15, ${theme.palette.mode === 'dark' ? 0 : 0.1})`, + color: theme.palette.mode === 'dark' ? '#ff4343' : '#750f0f', + }, +})); + +let promiseTimeout: any; +function validateName(username: string): Promise { + const existingUsers = rows.map((row) => row.name.toLowerCase()); + + return new Promise((resolve) => { + promiseTimeout = setTimeout(() => { + resolve(existingUsers.indexOf(username.toLowerCase()) === -1); + }, Math.random() * 500 + 100); // simulate network latency + }); +} + +export default function ValidateServerNameGrid() { + const apiRef = useGridApiRef(); + + const keyStrokeTimeoutRef = React.useRef(); + + const preProcessEditCellProps = (params: GridPreProcessEditCellProps) => + new Promise((resolve) => { + clearTimeout(promiseTimeout); + clearTimeout(keyStrokeTimeoutRef.current); + + // basic debouncing here + keyStrokeTimeoutRef.current = setTimeout(async () => { + const isValid = await validateName(params.props.value!.toString()); + resolve({ ...params.props, error: !isValid }); + }, 100); + }); + + const columns: GridColumns = [ + { + field: 'name', + headerName: 'MUI Contributor', + width: 180, + editable: true, + preProcessEditCellProps, + }, + ]; + + React.useEffect(() => { + return () => { + clearTimeout(promiseTimeout); + clearTimeout(keyStrokeTimeoutRef.current); + }; + }, []); + + return ( + + params.row.id === 5} + experimentalFeatures={{ preventCommitWhileValidating: true }} // Prevents a 2nd call to preProcessEditCellProps while waiting for the 1st + /> + + ); +} + +const rows: GridRowsProp = [ + { + id: 1, + name: 'Damien', + }, + { + id: 2, + name: 'Olivier', + }, + { + id: 3, + name: 'Danail', + }, + { + id: 4, + name: 'Matheus', + }, + { + id: 5, + name: 'You?', + }, +]; diff --git a/docs/data/data-grid/editing-legacy/ValidateServerNameGrid.tsx.preview b/docs/data/data-grid/editing-legacy/ValidateServerNameGrid.tsx.preview new file mode 100644 index 00000000000..c3f776e7c20 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/ValidateServerNameGrid.tsx.preview @@ -0,0 +1,9 @@ + + params.row.id === 5} + experimentalFeatures={{ preventCommitWhileValidating: true }} // Prevents a 2nd call to preProcessEditCellProps while waiting for the 1st + /> + \ No newline at end of file diff --git a/docs/data/data-grid/editing-legacy/ValueGetterSetterGrid.js b/docs/data/data-grid/editing-legacy/ValueGetterSetterGrid.js new file mode 100644 index 00000000000..9480c997264 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/ValueGetterSetterGrid.js @@ -0,0 +1,41 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +function getFullName(params) { + return `${params.row.firstName || ''} ${params.row.lastName || ''}`; +} + +function setFullName(params) { + const [firstName, lastName] = params.value.toString().split(' '); + return { ...params.row, firstName, lastName }; +} + +export default function ValueGetterSetterGrid() { + return ( +
+ +
+ ); +} + +const columns = [ + { field: 'firstName', headerName: 'First name', width: 130, editable: true }, + { field: 'lastName', headerName: 'Last name', width: 130, editable: true }, + { + field: 'fullName', + headerName: 'Full name', + width: 160, + editable: true, + valueGetter: getFullName, + valueSetter: setFullName, + sortComparator: (v1, v2) => v1.toString().localeCompare(v2.toString()), + }, +]; + +const defaultRows = [ + { id: 1, lastName: 'Snow', firstName: 'Jon' }, + { id: 2, lastName: 'Lannister', firstName: 'Cersei' }, + { id: 3, lastName: 'Lannister', firstName: 'Jaime' }, + { id: 4, lastName: 'Stark', firstName: 'Arya' }, + { id: 5, lastName: 'Targaryen', firstName: 'Daenerys' }, +]; diff --git a/docs/data/data-grid/editing-legacy/ValueGetterSetterGrid.tsx b/docs/data/data-grid/editing-legacy/ValueGetterSetterGrid.tsx new file mode 100644 index 00000000000..822a2d590e8 --- /dev/null +++ b/docs/data/data-grid/editing-legacy/ValueGetterSetterGrid.tsx @@ -0,0 +1,46 @@ +import * as React from 'react'; +import { + DataGrid, + GridColDef, + GridValueGetterParams, + GridValueSetterParams, +} from '@mui/x-data-grid'; + +function getFullName(params: GridValueGetterParams) { + return `${params.row.firstName || ''} ${params.row.lastName || ''}`; +} + +function setFullName(params: GridValueSetterParams) { + const [firstName, lastName] = params.value!.toString().split(' '); + return { ...params.row, firstName, lastName }; +} + +export default function ValueGetterSetterGrid() { + return ( +
+ +
+ ); +} + +const columns: GridColDef[] = [ + { field: 'firstName', headerName: 'First name', width: 130, editable: true }, + { field: 'lastName', headerName: 'Last name', width: 130, editable: true }, + { + field: 'fullName', + headerName: 'Full name', + width: 160, + editable: true, + valueGetter: getFullName, + valueSetter: setFullName, + sortComparator: (v1, v2) => v1!.toString().localeCompare(v2!.toString()), + }, +]; + +const defaultRows = [ + { id: 1, lastName: 'Snow', firstName: 'Jon' }, + { id: 2, lastName: 'Lannister', firstName: 'Cersei' }, + { id: 3, lastName: 'Lannister', firstName: 'Jaime' }, + { id: 4, lastName: 'Stark', firstName: 'Arya' }, + { id: 5, lastName: 'Targaryen', firstName: 'Daenerys' }, +]; diff --git a/docs/data/data-grid/editing-legacy/ValueGetterSetterGrid.tsx.preview b/docs/data/data-grid/editing-legacy/ValueGetterSetterGrid.tsx.preview new file mode 100644 index 00000000000..ae3bc510d2f --- /dev/null +++ b/docs/data/data-grid/editing-legacy/ValueGetterSetterGrid.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/editing-legacy/editing.md b/docs/data/data-grid/editing-legacy/editing.md new file mode 100644 index 00000000000..8cd9e6ad71c --- /dev/null +++ b/docs/data/data-grid/editing-legacy/editing.md @@ -0,0 +1,278 @@ +--- +title: Data Grid - Editing (legacy) +--- + +# Data Grid - Editing (legacy) + +

The data grid has built-in edit capabilities that you can customize to your needs.

+ +## Cell editing + +To enable cell editing within a column, set the `editable` property in the `GridColDef` object to `true` to allow editing cells of this column. + +```tsx + +``` + +{{"demo": "BasicEditingGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +### Start editing + +If a cell is editable and has focus, any of the following interactions will start the edit mode: + +- A Enter keydown +- A Backspace or Delete keydown. It will also delete the value and stops the edit mode instantly. +- A keydown of any printable key, for instance `a`, `E`, `0`, or `$` +- A double click on the cell +- A call to `apiRef.current.setCellMode(id, field, 'edit')`. + + ```tsx + /** + * Set the cellMode of a cell. + * @param GridRowId + * @param string + * @param 'edit' | 'view' + */ + setCellMode: (id: GridRowId, field: string, mode: GridCellMode) => void; + ``` + +### Stop editing + +If a cell is in edit mode and has focus, any of the following interactions will stop the edit mode: + +- A Escape keydown. It will also roll back changes done in the value of the cell. +- A Tab keydown. It will also save and goes to the next cell on the same row. +- A Enter keydown. It will also save and goes to the next cell on the same column. +- A mousedown outside the cell +- A call to `apiRef.current.setCellMode(id, field, 'view')`. + +### Control cell editability + +In addition to the `editable` flag on columns, you can enable or disable editing of individual cells using the `isCellEditable` prop. + +In this demo, only the rows with an even `Age` value are editable. +The editable cells have a green background for better visibility. + +{{"demo": "IsCellEditableGrid.js", "bg": "inline"}} + +### Controlled editing + +The `editRowsModel` prop lets you control the editing state. +You can use the `onEditRowsModelChange` callback to control the `GridEditRowsModel` state. + +{{"demo": "CellEditControlGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +### Saving nested structures + +If you are using a `valueGetter` to extract the value from a nested object, then you must also provides a `valueSetter`. +The `valueGetter` receives the row object and must return the value to be displayed in the cell. +The `valueSetter`, in turn, receives the new value and returns the updated row. + +The following demo shows how these two functions can be used: + +{{"demo": "ValueGetterSetterGrid.js", "bg": "inline"}} + +> Calling the `valueSetter` is the last step in the saving process. +> The [validation](/x/react-data-grid/editing/#client-side-validation) will still be called with the values before they pass through the setter. + +### Client-side validation + +To validate the value of a cell, add the `preProcessEditCellProps` callback to the [column definition](/x/api/data-grid/grid-col-def/) of the field you wish to validate. +Once it is called, you can validate the value provided in `params.props.value`. +Then, return a new object containing `params.props` along with `error` attribute set to true or false. +If the `error` attribute is true, the value will never be committed. + +```tsx +const columns: GridColDef[] = [ + { + field: 'firstName', + preProcessEditCellProps: (params: GridPreProcessEditCellProps) => { + const hasError = params.props.value.length < 3; + return { ...params.props, error: hasError }; + }, + }, +]; +``` + +Here is an example implementing an email validation: + +{{"demo": "ValidateRowModelControlGrid.js", "bg": "inline"}} + +> Alternatively, you can use the `GridEditRowsModel` state mentioned in the [Controlled editing](#controlled-editing) section. +> However, one limitation of this approach is that it does not work with the `singleSelect` column type. + +### Server-side validation + +Server-side validation works like [client-side validation](#client-side-validation). +The only difference is that when you call `preProcessEditCellProps`, you must return a promise. +Once the value is validated in the server, the promise should be resolved with a new object containing the `error` attribute set to true or false. +The grid will wait for the promise to be resolved before exiting the edit mode. + + + +> By default, `preProcessEditCellProps` is called on each value change and during commit. +> With column types that automatically commit the value after a selection (e.g. `singleSelect`) this means that the callback will be called twice and with the wrong value in the second call. +> To avoid this inconsistency, enable the `preventCommitWhileValidating` flag available in the experimental features. +> +> ```jsx +> +> ``` +> +> It's labeled as experimental because in v6 this flag will become the default behavior. + +This demo shows how you can validate a username asynchronously and prevent the user from committing the value while validating. +It's using `DataGridPro` but the same approach can be used with `DataGrid`. + +{{"demo": "ValidateServerNameGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +### Server-side persistence + +If you want to send the updated data to your server, you can use the `onCellEditCommit` which is fired just before committing the new cell value to the grid. + +You can then decide if you want to send the whole row or only the modified fields. + +{{"demo": "CellEditServerSidePersistence.js", "bg": "inline", "defaultCodeOpen": false}} + +### Custom edit component + +To customize the edit component of a column, use the `renderEditCell` attribute available in the `GridColDef`. + +The demo lets you edit the ratings by double-clicking the cell. + +{{"demo": "RenderRatingEditCellGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +### Edit using external button [](https://mui.com/store/items/mui-x-pro/) + +You can override the default [start editing](#start-editing) triggers using the [`event.defaultMuiPrevented`](/x/react-data-grid/events/#disabling-the-default-behavior) on the synthetic React events. + +{{"demo": "StartEditButtonGrid.js", "bg": "inline", "disableAd": true}} + +### Events [](https://mui.com/store/items/mui-x-pro/) + +The editing feature leverages the event capability of the grid and the apiRef. +You can import the following events to customize the editing experience: + +- `cellEditStart`: triggered when a cell enters edit mode. +- `cellEditStop`: triggered when a cell returns to view mode. +- `cellEditCommit`: triggered when a new value is committed. +- `editCellPropsChange`: triggered when a props passed to the edit cell component are changed. + +You can use event catching to add a callback after an event while ignoring its triggers. + +The demo shows how to catch the start and end edit events in order to log which cell has been edited in an info message: + +{{"demo": "CatchEditingEventsGrid.js", "bg": "inline", "disableAd": true}} + +## Row editing + +Row editing lets you edit all cells in a given row simultaneously. +It supports most of the same features as those available for [cell editing](/x/react-data-grid/editing/#cell-editing). +To enable it, change the edit mode to `"row"` using the `editMode` prop, then set to `true` the `editable` property in the `GridColDef` object of those columns that should be editable. + +```tsx + +``` + +{{"demo": "BasicRowEditingGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +### Start editing + +You can start editing a cell using any of the following interactions: + +- A Enter keydown +- A double click on the cell +- A call to `apiRef.current.setRowMode(id, 'edit')`. + + ```tsx + /** + * Sets the mode of a row. + * @param {GridRowId} id The id of the row. + * @param {GridRowMode} mode Can be: `"edit"`, `"view"`. + */ + setRowMode: (id: GridRowId, mode: GridRowMode) => void; + ``` + +### Stop editing + +You can stop editing a cell using any of the following interactions: + +- A Escape keydown. This will also roll back any changes made to the row. +- A Enter keydown. This will also save the value and move the focus to the cell in the next row of the same column. +- A mouse click outside the row +- A call to `apiRef.current.setRowMode(id, 'view')`. + +### Controlled editing + +The `editRowsModel` prop lets you control the editing state. +You can handle the `onEditRowsModelChange` callback to control the `GridEditRowsModel` state. + +{{"demo": "RowEditControlGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +### Conditional validation [](https://mui.com/store/items/mui-x-pro/) + +Having all cells of a row in edit mode allows validating a field based on the value of another one +To do this, you will first need to add a `preProcessEditCellProps` callback to the [column definition](/x/api/data-grid/grid-col-def/). +When all cells in a row are in edit mode, you can validate fields by comparing their values. +Return a new object contaning `params.props` and the `error` attribute with the validation status. +Once at the least one field has the `error` attribute equals to true no new value will be committed. + +:::warning +Server-side validation works like [client-side validation](#client-side-validation). +The only difference is that when you call `preProcessEditCellProps`, you must return a promise. +Once the value is validated in the server, the promise should be resolved with a new object containing the `error` attribute set to true or false. +The grid will wait for the promise to be resolved before exiting the edit mode. +::: + +The following demo requires a value for the **Payment method** column if the **Is paid?** column is checked. + +{{"demo": "ConditionalValidationGrid.js", "disableAd": true, "bg": "inline", "defaultCodeOpen": false}} + +> The conditional validation can also be implemented with the [controlled editing](#controlled-editing-2). +> This approach can be used in the free version of the DataGrid. +> The only limitation is that it does not work with the `singleSelect` column type. + +### Control with external buttons [](https://mui.com/store/items/mui-x-pro/) + +You can [disable the default behavior](/x/react-data-grid/events/#disabling-the-default-behavior) of the grid and control the row edit using external buttons. + +Here is how you can create a full-featured CRUD: + +{{"demo": "FullFeaturedCrudGrid.js", "bg": "inline", "disableAd": true}} + +### Saving rows with nested structures + +You can save columns that make use of `valueGetter` by adding a `valueSetter`. +The same [approach](/x/react-data-grid/editing/#saving-nested-structures) from the cell editing mode can be used here. +Note that the `valueSetter` will be called for each field. + +### Server-side persistence [](https://mui.com/store/items/mui-x-pro/) + +If you want to send the updated data to your server, you can use `onRowEditCommit` which is fired just before committing the new cell value to the grid. + +To access the new values for the row, use `apiRef.current.getEditRowsModel` to enable edit mode on all rows, then use the ID provided to get only the values for the row that was committed. + +You can then decide if you want to send the whole row or only the modified fields by checking them against the previous row values. + +{{"demo": "RowEditServerSidePersistence.js", "disableAd": true, "bg": "inline", "defaultCodeOpen": false}} + +### Events [](https://mui.com/store/items/mui-x-pro/) + +You can import the following events to customize the editing experience: + +- `rowEditStart`: triggered when a row enters edit mode. +- `rowEditStop`: triggered when a row returns to view mode. +- `rowEditCommit`: triggered when new row values are committed. +- `editCellPropsChange`: triggered when the props passed to the edit cell component are changed. + +## apiRef [](https://mui.com/store/items/mui-x-pro/) + +:::warning +Only use this API as the last option. Give preference to the props to control the grid. +::: + +{{"demo": "EditApiNoSnap.js", "bg": "inline", "hideToolbar": true}} + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/editing/AskConfirmationBeforeSave.js b/docs/data/data-grid/editing/AskConfirmationBeforeSave.js new file mode 100644 index 00000000000..0a820cd6a6b --- /dev/null +++ b/docs/data/data-grid/editing/AskConfirmationBeforeSave.js @@ -0,0 +1,193 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; +import Snackbar from '@mui/material/Snackbar'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import Button from '@mui/material/Button'; +import Alert from '@mui/material/Alert'; + +const useFakeMutation = () => { + return React.useCallback( + (user) => + new Promise((resolve, reject) => + setTimeout(() => { + if (user.name?.trim() === '') { + reject(); + } else { + resolve(user); + } + }, 200), + ), + [], + ); +}; + +function computeMutation(newRow, oldRow) { + if (newRow.name !== oldRow.name) { + return `Name from '${oldRow.name}' to '${newRow.name}'`; + } + if (newRow.age !== oldRow.age) { + return `Age from '${oldRow.age || ''}' to '${newRow.age || ''}'`; + } + return null; +} + +export default function AskConfirmationBeforeSave() { + const mutateRow = useFakeMutation(); + const noButtonRef = React.useRef(null); + const [promiseArguments, setPromiseArguments] = React.useState(null); + + const [snackbar, setSnackbar] = React.useState(null); + + const handleCloseSnackbar = () => setSnackbar(null); + + const processRowUpdate = React.useCallback( + (newRow, oldRow) => + new Promise((resolve, reject) => { + const mutation = computeMutation(newRow, oldRow); + if (mutation) { + // Save the arguments to resolve or reject the promise later + setPromiseArguments({ resolve, reject, newRow, oldRow }); + } else { + resolve(oldRow); // Nothing was changed + } + }), + [], + ); + + const handleNo = () => { + const { oldRow, resolve } = promiseArguments; + resolve(oldRow); // Resolve with the old row to not update the internal state + setPromiseArguments(null); + }; + + const handleYes = async () => { + const { newRow, oldRow, reject, resolve } = promiseArguments; + + try { + // Make the HTTP request to save in the backend + const response = await mutateRow(newRow); + setSnackbar({ children: 'User successfully saved', severity: 'success' }); + resolve(response); + setPromiseArguments(null); + } catch (error) { + setSnackbar({ children: "Name can't be empty", severity: 'error' }); + reject(oldRow); + setPromiseArguments(null); + } + }; + + const handleEntered = () => { + // The `autoFocus` is not used because, if used, the same Enter that saves + // the cell triggers "No". Instead, we manually focus the "No" button once + // the dialog is fully open. + // noButtonRef.current?.focus(); + }; + + const renderConfirmDialog = () => { + if (!promiseArguments) { + return null; + } + + const { newRow, oldRow } = promiseArguments; + const mutation = computeMutation(newRow, oldRow); + + return ( + + Are you sure? + + {`Pressing 'Yes' will change ${mutation}.`} + + + + + + + ); + }; + + return ( +
+ {renderConfirmDialog()} + + {!!snackbar && ( + + + + )} +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing/AskConfirmationBeforeSave.tsx b/docs/data/data-grid/editing/AskConfirmationBeforeSave.tsx new file mode 100644 index 00000000000..549797f5c63 --- /dev/null +++ b/docs/data/data-grid/editing/AskConfirmationBeforeSave.tsx @@ -0,0 +1,210 @@ +import * as React from 'react'; +import { + DataGrid, + GridRowModel, + GridColumns, + GridRowId, + GridRowsProp, +} from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; +import Snackbar from '@mui/material/Snackbar'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import Button from '@mui/material/Button'; +import Alert, { AlertProps } from '@mui/material/Alert'; + +interface User { + name: string; + age: number; + id: GridRowId; + dateCreated: Date; + lastLogin: Date; +} + +const useFakeMutation = () => { + return React.useCallback( + (user: Partial) => + new Promise>((resolve, reject) => + setTimeout(() => { + if (user.name?.trim() === '') { + reject(); + } else { + resolve(user); + } + }, 200), + ), + [], + ); +}; + +function computeMutation(newRow: GridRowModel, oldRow: GridRowModel) { + if (newRow.name !== oldRow.name) { + return `Name from '${oldRow.name}' to '${newRow.name}'`; + } + if (newRow.age !== oldRow.age) { + return `Age from '${oldRow.age || ''}' to '${newRow.age || ''}'`; + } + return null; +} + +export default function AskConfirmationBeforeSave() { + const mutateRow = useFakeMutation(); + const noButtonRef = React.useRef(null); + const [promiseArguments, setPromiseArguments] = React.useState(null); + + const [snackbar, setSnackbar] = React.useState | null>(null); + + const handleCloseSnackbar = () => setSnackbar(null); + + const processRowUpdate = React.useCallback( + (newRow: GridRowModel, oldRow: GridRowModel) => + new Promise((resolve, reject) => { + const mutation = computeMutation(newRow, oldRow); + if (mutation) { + // Save the arguments to resolve or reject the promise later + setPromiseArguments({ resolve, reject, newRow, oldRow }); + } else { + resolve(oldRow); // Nothing was changed + } + }), + [], + ); + + const handleNo = () => { + const { oldRow, resolve } = promiseArguments; + resolve(oldRow); // Resolve with the old row to not update the internal state + setPromiseArguments(null); + }; + + const handleYes = async () => { + const { newRow, oldRow, reject, resolve } = promiseArguments; + + try { + // Make the HTTP request to save in the backend + const response = await mutateRow(newRow); + setSnackbar({ children: 'User successfully saved', severity: 'success' }); + resolve(response); + setPromiseArguments(null); + } catch (error) { + setSnackbar({ children: "Name can't be empty", severity: 'error' }); + reject(oldRow); + setPromiseArguments(null); + } + }; + + const handleEntered = () => { + // The `autoFocus` is not used because, if used, the same Enter that saves + // the cell triggers "No". Instead, we manually focus the "No" button once + // the dialog is fully open. + // noButtonRef.current?.focus(); + }; + + const renderConfirmDialog = () => { + if (!promiseArguments) { + return null; + } + + const { newRow, oldRow } = promiseArguments; + const mutation = computeMutation(newRow, oldRow); + + return ( + + Are you sure? + + {`Pressing 'Yes' will change ${mutation}.`} + + + + + + + ); + }; + + return ( +
+ {renderConfirmDialog()} + + {!!snackbar && ( + + + + )} +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing/AskConfirmationBeforeSave.tsx.preview b/docs/data/data-grid/editing/AskConfirmationBeforeSave.tsx.preview new file mode 100644 index 00000000000..c3fbabd9997 --- /dev/null +++ b/docs/data/data-grid/editing/AskConfirmationBeforeSave.tsx.preview @@ -0,0 +1,12 @@ +{renderConfirmDialog()} + +{!!snackbar && ( + + + +)} \ No newline at end of file diff --git a/docs/data/data-grid/editing/AutoStopEditComponent.js b/docs/data/data-grid/editing/AutoStopEditComponent.js new file mode 100644 index 00000000000..0d1be495140 --- /dev/null +++ b/docs/data/data-grid/editing/AutoStopEditComponent.js @@ -0,0 +1,93 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Select from '@mui/material/Select'; +import { DataGrid, useGridApiContext } from '@mui/x-data-grid'; + +function SelectEditInputCell(props) { + const { id, value, field } = props; + const apiRef = useGridApiContext(); + + const handleChange = async (event) => { + await apiRef.current.setEditCellValue({ id, field, value: event.target.value }); + apiRef.current.stopCellEditMode({ id, field }); + }; + + return ( + + ); +} + +SelectEditInputCell.propTypes = { + /** + * The column field of the cell that triggered the event. + */ + field: PropTypes.string.isRequired, + /** + * The grid row id. + */ + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + /** + * The cell value, but if the column has valueGetter, use getValue. + */ + value: PropTypes.any, +}; + +const renderSelectEditInputCell = (params) => { + return ; +}; + +export default function AutoStopEditComponent() { + return ( +
+ +
+ ); +} + +const columns = [ + { + field: 'name', + headerName: 'Name', + width: 120, + }, + { + field: 'role', + headerName: 'Role', + renderEditCell: renderSelectEditInputCell, + editable: true, + width: 180, + }, +]; + +const rows = [ + { + id: 1, + name: 'Olivier', + role: 'Back-end Developer', + }, + { + id: 2, + name: 'Danail', + role: 'UX Designer', + }, + { + id: 3, + name: 'Matheus', + role: 'Front-end Developer', + }, +]; diff --git a/docs/data/data-grid/editing/AutoStopEditComponent.tsx b/docs/data/data-grid/editing/AutoStopEditComponent.tsx new file mode 100644 index 00000000000..9f799e886da --- /dev/null +++ b/docs/data/data-grid/editing/AutoStopEditComponent.tsx @@ -0,0 +1,82 @@ +import * as React from 'react'; +import Select, { SelectChangeEvent } from '@mui/material/Select'; +import { + DataGrid, + GridRenderCellParams, + GridColDef, + useGridApiContext, +} from '@mui/x-data-grid'; + +function SelectEditInputCell(props: GridRenderCellParams) { + const { id, value, field } = props; + const apiRef = useGridApiContext(); + + const handleChange = async (event: SelectChangeEvent) => { + await apiRef.current.setEditCellValue({ id, field, value: event.target.value }); + apiRef.current.stopCellEditMode({ id, field }); + }; + + return ( + + ); +} + +const renderSelectEditInputCell: GridColDef['renderCell'] = (params) => { + return ; +}; + +export default function AutoStopEditComponent() { + return ( +
+ +
+ ); +} + +const columns = [ + { + field: 'name', + headerName: 'Name', + width: 120, + }, + { + field: 'role', + headerName: 'Role', + renderEditCell: renderSelectEditInputCell, + editable: true, + width: 180, + }, +]; + +const rows = [ + { + id: 1, + name: 'Olivier', + role: 'Back-end Developer', + }, + { + id: 2, + name: 'Danail', + role: 'UX Designer', + }, + { + id: 3, + name: 'Matheus', + role: 'Front-end Developer', + }, +]; diff --git a/docs/data/data-grid/editing/AutoStopEditComponent.tsx.preview b/docs/data/data-grid/editing/AutoStopEditComponent.tsx.preview new file mode 100644 index 00000000000..5639d6d287f --- /dev/null +++ b/docs/data/data-grid/editing/AutoStopEditComponent.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/editing/BasicEditingGrid.js b/docs/data/data-grid/editing/BasicEditingGrid.js new file mode 100644 index 00000000000..6c39afe8de9 --- /dev/null +++ b/docs/data/data-grid/editing/BasicEditingGrid.js @@ -0,0 +1,76 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function BasicEditingGrid() { + return ( +
+ +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing/BasicEditingGrid.tsx b/docs/data/data-grid/editing/BasicEditingGrid.tsx new file mode 100644 index 00000000000..ddf283fb820 --- /dev/null +++ b/docs/data/data-grid/editing/BasicEditingGrid.tsx @@ -0,0 +1,76 @@ +import * as React from 'react'; +import { DataGrid, GridColumns, GridRowsProp } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function BasicEditingGrid() { + return ( +
+ +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing/BasicEditingGrid.tsx.preview b/docs/data/data-grid/editing/BasicEditingGrid.tsx.preview new file mode 100644 index 00000000000..5639d6d287f --- /dev/null +++ b/docs/data/data-grid/editing/BasicEditingGrid.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/editing/BasicRowEditingGrid.js b/docs/data/data-grid/editing/BasicRowEditingGrid.js new file mode 100644 index 00000000000..ecdfa8d7a4f --- /dev/null +++ b/docs/data/data-grid/editing/BasicRowEditingGrid.js @@ -0,0 +1,77 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function BasicRowEditingGrid() { + return ( +
+ +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing/BasicRowEditingGrid.tsx b/docs/data/data-grid/editing/BasicRowEditingGrid.tsx new file mode 100644 index 00000000000..dd31f6b4f22 --- /dev/null +++ b/docs/data/data-grid/editing/BasicRowEditingGrid.tsx @@ -0,0 +1,77 @@ +import * as React from 'react'; +import { DataGrid, GridColumns, GridRowsProp } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function BasicRowEditingGrid() { + return ( +
+ +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing/BasicRowEditingGrid.tsx.preview b/docs/data/data-grid/editing/BasicRowEditingGrid.tsx.preview new file mode 100644 index 00000000000..b85312ba0d4 --- /dev/null +++ b/docs/data/data-grid/editing/BasicRowEditingGrid.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/editing/ConditionalValidationGrid.js b/docs/data/data-grid/editing/ConditionalValidationGrid.js new file mode 100644 index 00000000000..20833c0c347 --- /dev/null +++ b/docs/data/data-grid/editing/ConditionalValidationGrid.js @@ -0,0 +1,99 @@ +import * as React from 'react'; +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import { DataGrid } from '@mui/x-data-grid'; +import { randomPrice } from '@mui/x-data-grid-generator'; + +const StyledBox = styled(Box)(({ theme }) => ({ + height: 300, + width: '100%', + '& .MuiDataGrid-cell--editing': { + backgroundColor: 'rgb(255,215,115, 0.19)', + color: '#1a3e72', + '& .MuiInputBase-root': { + height: '100%', + }, + }, + '& .Mui-error': { + backgroundColor: `rgb(126,10,15, ${theme.palette.mode === 'dark' ? 0 : 0.1})`, + color: theme.palette.error.main, + }, +})); + +const rows = [ + { + id: 1, + expense: 'Light bill', + price: randomPrice(0, 1000), + dueAt: new Date(2021, 6, 8), + isPaid: false, + paymentMethod: '', + }, + { + id: 2, + expense: 'Rent', + price: randomPrice(0, 1000), + dueAt: new Date(2021, 7, 1), + isPaid: false, + paymentMethod: '', + }, + { + id: 3, + expense: 'Car insurance', + price: randomPrice(0, 1000), + dueAt: new Date(2021, 7, 4), + isPaid: true, + paymentMethod: 'Wire transfer', + }, +]; + +export default function ConditionalValidationGrid() { + const columns = [ + { field: 'expense', headerName: 'Expense', width: 160, editable: true }, + { + field: 'price', + headerName: 'Price', + type: 'number', + width: 120, + editable: true, + }, + { + field: 'dueAt', + headerName: 'Due at', + type: 'date', + width: 120, + editable: true, + }, + { + field: 'isPaid', + headerName: 'Is paid?', + type: 'boolean', + width: 140, + editable: true, + }, + { + field: 'paymentMethod', + headerName: 'Payment method', + type: 'singleSelect', + valueOptions: ['Credit card', 'Wire transfer', 'Cash'], + width: 160, + editable: true, + preProcessEditCellProps: (params) => { + const isPaidProps = params.otherFieldsProps.isPaid; + const hasError = isPaidProps.value && !params.props.value; + return { ...params.props, error: hasError }; + }, + }, + ]; + + return ( + + + + ); +} diff --git a/docs/data/data-grid/editing/ConditionalValidationGrid.tsx b/docs/data/data-grid/editing/ConditionalValidationGrid.tsx new file mode 100644 index 00000000000..6497dc81a82 --- /dev/null +++ b/docs/data/data-grid/editing/ConditionalValidationGrid.tsx @@ -0,0 +1,99 @@ +import * as React from 'react'; +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import { DataGrid, GridColumns, GridRowsProp } from '@mui/x-data-grid'; +import { randomPrice } from '@mui/x-data-grid-generator'; + +const StyledBox = styled(Box)(({ theme }) => ({ + height: 300, + width: '100%', + '& .MuiDataGrid-cell--editing': { + backgroundColor: 'rgb(255,215,115, 0.19)', + color: '#1a3e72', + '& .MuiInputBase-root': { + height: '100%', + }, + }, + '& .Mui-error': { + backgroundColor: `rgb(126,10,15, ${theme.palette.mode === 'dark' ? 0 : 0.1})`, + color: theme.palette.error.main, + }, +})); + +const rows: GridRowsProp = [ + { + id: 1, + expense: 'Light bill', + price: randomPrice(0, 1000), + dueAt: new Date(2021, 6, 8), + isPaid: false, + paymentMethod: '', + }, + { + id: 2, + expense: 'Rent', + price: randomPrice(0, 1000), + dueAt: new Date(2021, 7, 1), + isPaid: false, + paymentMethod: '', + }, + { + id: 3, + expense: 'Car insurance', + price: randomPrice(0, 1000), + dueAt: new Date(2021, 7, 4), + isPaid: true, + paymentMethod: 'Wire transfer', + }, +]; + +export default function ConditionalValidationGrid() { + const columns: GridColumns = [ + { field: 'expense', headerName: 'Expense', width: 160, editable: true }, + { + field: 'price', + headerName: 'Price', + type: 'number', + width: 120, + editable: true, + }, + { + field: 'dueAt', + headerName: 'Due at', + type: 'date', + width: 120, + editable: true, + }, + { + field: 'isPaid', + headerName: 'Is paid?', + type: 'boolean', + width: 140, + editable: true, + }, + { + field: 'paymentMethod', + headerName: 'Payment method', + type: 'singleSelect', + valueOptions: ['Credit card', 'Wire transfer', 'Cash'], + width: 160, + editable: true, + preProcessEditCellProps: (params) => { + const isPaidProps = params.otherFieldsProps!.isPaid; + const hasError = isPaidProps.value && !params.props.value; + return { ...params.props, error: hasError }; + }, + }, + ]; + + return ( + + + + ); +} diff --git a/docs/data/data-grid/editing/ConditionalValidationGrid.tsx.preview b/docs/data/data-grid/editing/ConditionalValidationGrid.tsx.preview new file mode 100644 index 00000000000..7f332e83ebe --- /dev/null +++ b/docs/data/data-grid/editing/ConditionalValidationGrid.tsx.preview @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/docs/data/data-grid/editing/CustomEditComponent.js b/docs/data/data-grid/editing/CustomEditComponent.js new file mode 100644 index 00000000000..5c280921cbd --- /dev/null +++ b/docs/data/data-grid/editing/CustomEditComponent.js @@ -0,0 +1,100 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import Rating from '@mui/material/Rating'; +import { DataGrid, useGridApiContext } from '@mui/x-data-grid'; + +function renderRating(params) { + return ; +} + +renderRating.propTypes = { + /** + * The cell value, but if the column has valueGetter, use getValue. + */ + value: PropTypes.number, +}; + +function RatingEditInputCell(props) { + const { id, value, field } = props; + const apiRef = useGridApiContext(); + + const handleChange = (event, newValue) => { + apiRef.current.setEditCellValue({ id, field, value: newValue }); + }; + + const handleRef = (element) => { + if (element) { + const input = element.querySelector(`input[value="${value}"]`); + + input?.focus(); + } + }; + + return ( + + + + ); +} + +RatingEditInputCell.propTypes = { + /** + * The column field of the cell that triggered the event. + */ + field: PropTypes.string.isRequired, + /** + * The grid row id. + */ + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + /** + * The cell value, but if the column has valueGetter, use getValue. + */ + value: PropTypes.number, +}; + +const renderRatingEditInputCell = (params) => { + return ; +}; + +export default function CustomEditComponent() { + return ( +
+ +
+ ); +} + +const columns = [ + { + field: 'places', + headerName: 'Places', + width: 120, + }, + { + field: 'rating', + headerName: 'Rating', + renderCell: renderRating, + renderEditCell: renderRatingEditInputCell, + editable: true, + width: 180, + type: 'number', + }, +]; + +const rows = [ + { id: 1, places: 'Barcelona', rating: 5 }, + { id: 2, places: 'Rio de Janeiro', rating: 4 }, + { id: 3, places: 'London', rating: 3 }, + { id: 4, places: 'New York', rating: 2 }, +]; diff --git a/docs/data/data-grid/editing/CustomEditComponent.tsx b/docs/data/data-grid/editing/CustomEditComponent.tsx new file mode 100644 index 00000000000..843d16c27b0 --- /dev/null +++ b/docs/data/data-grid/editing/CustomEditComponent.tsx @@ -0,0 +1,83 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Rating from '@mui/material/Rating'; +import { + DataGrid, + GridRenderCellParams, + GridColDef, + useGridApiContext, +} from '@mui/x-data-grid'; + +function renderRating(params: GridRenderCellParams) { + return ; +} + +function RatingEditInputCell(props: GridRenderCellParams) { + const { id, value, field } = props; + const apiRef = useGridApiContext(); + + const handleChange = (event: React.SyntheticEvent, newValue: number | null) => { + apiRef.current.setEditCellValue({ id, field, value: newValue }); + }; + + const handleRef = (element: HTMLSpanElement) => { + if (element) { + const input = element.querySelector( + `input[value="${value}"]`, + ); + input?.focus(); + } + }; + + return ( + + + + ); +} + +const renderRatingEditInputCell: GridColDef['renderCell'] = (params) => { + return ; +}; + +export default function CustomEditComponent() { + return ( +
+ +
+ ); +} + +const columns = [ + { + field: 'places', + headerName: 'Places', + width: 120, + }, + { + field: 'rating', + headerName: 'Rating', + renderCell: renderRating, + renderEditCell: renderRatingEditInputCell, + editable: true, + width: 180, + type: 'number', + }, +]; + +const rows = [ + { id: 1, places: 'Barcelona', rating: 5 }, + { id: 2, places: 'Rio de Janeiro', rating: 4 }, + { id: 3, places: 'London', rating: 3 }, + { id: 4, places: 'New York', rating: 2 }, +]; diff --git a/docs/data/data-grid/editing/CustomEditComponent.tsx.preview b/docs/data/data-grid/editing/CustomEditComponent.tsx.preview new file mode 100644 index 00000000000..5639d6d287f --- /dev/null +++ b/docs/data/data-grid/editing/CustomEditComponent.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/editing/DisableStopEditModeOnFocusOut.js b/docs/data/data-grid/editing/DisableStopEditModeOnFocusOut.js new file mode 100644 index 00000000000..c260b9d820d --- /dev/null +++ b/docs/data/data-grid/editing/DisableStopEditModeOnFocusOut.js @@ -0,0 +1,81 @@ +import * as React from 'react'; +import { DataGrid, GridCellEditStopReasons } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function DisableStopEditModeOnFocusOut() { + return ( +
+ { + if (params.reason === GridCellEditStopReasons.cellFocusOut) { + event.defaultMuiPrevented = true; + } + }} + /> +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing/DisableStopEditModeOnFocusOut.tsx b/docs/data/data-grid/editing/DisableStopEditModeOnFocusOut.tsx new file mode 100644 index 00000000000..f49b7d8e52d --- /dev/null +++ b/docs/data/data-grid/editing/DisableStopEditModeOnFocusOut.tsx @@ -0,0 +1,88 @@ +import * as React from 'react'; +import { + GridColumns, + GridRowsProp, + DataGrid, + GridCellEditStopParams, + GridCellEditStopReasons, + MuiEvent, +} from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function DisableStopEditModeOnFocusOut() { + return ( +
+ { + if (params.reason === GridCellEditStopReasons.cellFocusOut) { + event.defaultMuiPrevented = true; + } + }} + /> +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing/DisableStopEditModeOnFocusOut.tsx.preview b/docs/data/data-grid/editing/DisableStopEditModeOnFocusOut.tsx.preview new file mode 100644 index 00000000000..1a623087c09 --- /dev/null +++ b/docs/data/data-grid/editing/DisableStopEditModeOnFocusOut.tsx.preview @@ -0,0 +1,10 @@ + { + if (params.reason === GridCellEditStopReasons.cellFocusOut) { + event.defaultMuiPrevented = true; + } + }} +/> \ No newline at end of file diff --git a/docs/data/data-grid/editing/EditApiNoSnap.js b/docs/data/data-grid/editing/EditApiNoSnap.js new file mode 100644 index 00000000000..c52670aca19 --- /dev/null +++ b/docs/data/data-grid/editing/EditApiNoSnap.js @@ -0,0 +1,7 @@ +import React from 'react'; +import ApiDocs from 'docsx/src/modules/components/ApiDocs'; +import api from 'docsx/pages/x/api/data-grid/grid-new-editing-api.json'; + +export default function EditApiNoSnap() { + return ; +} diff --git a/docs/data/data-grid/editing/FullFeaturedCrudGrid.js b/docs/data/data-grid/editing/FullFeaturedCrudGrid.js new file mode 100644 index 00000000000..341196deefc --- /dev/null +++ b/docs/data/data-grid/editing/FullFeaturedCrudGrid.js @@ -0,0 +1,223 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import AddIcon from '@mui/icons-material/Add'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/DeleteOutlined'; +import SaveIcon from '@mui/icons-material/Save'; +import CancelIcon from '@mui/icons-material/Close'; +import { + GridRowModes, + DataGridPro, + GridToolbarContainer, + GridActionsCellItem, +} from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, + randomId, +} from '@mui/x-data-grid-generator'; + +const initialRows = [ + { + id: randomId(), + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; + +function EditToolbar(props) { + const { setRows, setRowModesModel } = props; + + const handleClick = () => { + const id = randomId(); + setRows((oldRows) => [...oldRows, { id, name: '', age: '', isNew: true }]); + setRowModesModel((oldModel) => ({ + ...oldModel, + [id]: { mode: GridRowModes.Edit, fieldToFocus: 'name' }, + })); + }; + + return ( + + + + ); +} + +EditToolbar.propTypes = { + setRowModesModel: PropTypes.func.isRequired, + setRows: PropTypes.func.isRequired, +}; + +export default function FullFeaturedCrudGrid() { + const [rows, setRows] = React.useState(initialRows); + const [rowModesModel, setRowModesModel] = React.useState({}); + + const handleRowEditStart = (params, event) => { + event.defaultMuiPrevented = true; + }; + + const handleRowEditStop = (params, event) => { + event.defaultMuiPrevented = true; + }; + + const handleEditClick = (id) => () => { + setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.Edit } }); + }; + + const handleSaveClick = (id) => () => { + setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } }); + }; + + const handleDeleteClick = (id) => () => { + setRows(rows.filter((row) => row.id !== id)); + }; + + const handleCancelClick = (id) => () => { + setRowModesModel({ + ...rowModesModel, + [id]: { mode: GridRowModes.View, ignoreModifications: true }, + }); + + const editedRow = rows.find((row) => row.id === id); + if (editedRow.isNew) { + setRows(rows.filter((row) => row.id !== id)); + } + }; + + const processRowUpdate = (newRow) => { + const updatedRow = { ...newRow, isNew: false }; + setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row))); + return updatedRow; + }; + + const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, + { + field: 'actions', + type: 'actions', + headerName: 'Actions', + width: 100, + cellClassName: 'actions', + getActions: ({ id }) => { + const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; + + if (isInEditMode) { + return [ + } + label="Save" + onClick={handleSaveClick(id)} + color="primary" + />, + } + label="Cancel" + className="textPrimary" + onClick={handleCancelClick(id)} + color="inherit" + />, + ]; + } + + return [ + } + label="Edit" + className="textPrimary" + onClick={handleEditClick(id)} + color="inherit" + />, + } + label="Delete" + onClick={handleDeleteClick(id)} + color="inherit" + />, + ]; + }, + }, + ]; + + return ( + + + + ); +} diff --git a/docs/data/data-grid/editing/FullFeaturedCrudGrid.tsx b/docs/data/data-grid/editing/FullFeaturedCrudGrid.tsx new file mode 100644 index 00000000000..d5a8c9272eb --- /dev/null +++ b/docs/data/data-grid/editing/FullFeaturedCrudGrid.tsx @@ -0,0 +1,235 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import AddIcon from '@mui/icons-material/Add'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/DeleteOutlined'; +import SaveIcon from '@mui/icons-material/Save'; +import CancelIcon from '@mui/icons-material/Close'; +import { + GridRowsProp, + GridRowModesModel, + GridRowModes, + DataGridPro, + GridColumns, + GridRowParams, + MuiEvent, + GridToolbarContainer, + GridActionsCellItem, + GridEventListener, + GridRowId, + GridRowModel, +} from '@mui/x-data-grid-pro'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, + randomId, +} from '@mui/x-data-grid-generator'; + +const initialRows: GridRowsProp = [ + { + id: randomId(), + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: randomId(), + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; + +interface EditToolbarProps { + setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void; + setRowModesModel: ( + newModel: (oldModel: GridRowModesModel) => GridRowModesModel, + ) => void; +} + +function EditToolbar(props: EditToolbarProps) { + const { setRows, setRowModesModel } = props; + + const handleClick = () => { + const id = randomId(); + setRows((oldRows) => [...oldRows, { id, name: '', age: '', isNew: true }]); + setRowModesModel((oldModel) => ({ + ...oldModel, + [id]: { mode: GridRowModes.Edit, fieldToFocus: 'name' }, + })); + }; + + return ( + + + + ); +} + +export default function FullFeaturedCrudGrid() { + const [rows, setRows] = React.useState(initialRows); + const [rowModesModel, setRowModesModel] = React.useState({}); + + const handleRowEditStart = ( + params: GridRowParams, + event: MuiEvent, + ) => { + event.defaultMuiPrevented = true; + }; + + const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => { + event.defaultMuiPrevented = true; + }; + + const handleEditClick = (id: GridRowId) => () => { + setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.Edit } }); + }; + + const handleSaveClick = (id: GridRowId) => () => { + setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } }); + }; + + const handleDeleteClick = (id: GridRowId) => () => { + setRows(rows.filter((row) => row.id !== id)); + }; + + const handleCancelClick = (id: GridRowId) => () => { + setRowModesModel({ + ...rowModesModel, + [id]: { mode: GridRowModes.View, ignoreModifications: true }, + }); + + const editedRow = rows.find((row) => row.id === id); + if (editedRow!.isNew) { + setRows(rows.filter((row) => row.id !== id)); + } + }; + + const processRowUpdate = (newRow: GridRowModel) => { + const updatedRow = { ...newRow, isNew: false }; + setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row))); + return updatedRow; + }; + + const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, + { + field: 'actions', + type: 'actions', + headerName: 'Actions', + width: 100, + cellClassName: 'actions', + getActions: ({ id }) => { + const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; + + if (isInEditMode) { + return [ + } + label="Save" + onClick={handleSaveClick(id)} + color="primary" + />, + } + label="Cancel" + className="textPrimary" + onClick={handleCancelClick(id)} + color="inherit" + />, + ]; + } + + return [ + } + label="Edit" + className="textPrimary" + onClick={handleEditClick(id)} + color="inherit" + />, + } + label="Delete" + onClick={handleDeleteClick(id)} + color="inherit" + />, + ]; + }, + }, + ]; + + return ( + + + + ); +} diff --git a/docs/data/data-grid/editing/FullFeaturedCrudGrid.tsx.preview b/docs/data/data-grid/editing/FullFeaturedCrudGrid.tsx.preview new file mode 100644 index 00000000000..4f5542f1c10 --- /dev/null +++ b/docs/data/data-grid/editing/FullFeaturedCrudGrid.tsx.preview @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/editing/IsCellEditableGrid.js b/docs/data/data-grid/editing/IsCellEditableGrid.js new file mode 100644 index 00000000000..7a580bc0a28 --- /dev/null +++ b/docs/data/data-grid/editing/IsCellEditableGrid.js @@ -0,0 +1,87 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function IsCellEditableGrid() { + return ( + + theme.palette.mode === 'dark' ? '#376331' : 'rgb(217 243 190)', + }, + }} + > + params.row.age % 2 === 0} + experimentalFeatures={{ newEditingApi: true }} + /> + + ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing/IsCellEditableGrid.tsx b/docs/data/data-grid/editing/IsCellEditableGrid.tsx new file mode 100644 index 00000000000..45bc59c9323 --- /dev/null +++ b/docs/data/data-grid/editing/IsCellEditableGrid.tsx @@ -0,0 +1,87 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid, GridColumns, GridRowsProp } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function IsCellEditableGrid() { + return ( + + theme.palette.mode === 'dark' ? '#376331' : 'rgb(217 243 190)', + }, + }} + > + params.row.age % 2 === 0} + experimentalFeatures={{ newEditingApi: true }} + /> + + ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing/IsCellEditableGrid.tsx.preview b/docs/data/data-grid/editing/IsCellEditableGrid.tsx.preview new file mode 100644 index 00000000000..30696b22f99 --- /dev/null +++ b/docs/data/data-grid/editing/IsCellEditableGrid.tsx.preview @@ -0,0 +1,6 @@ + params.row.age % 2 === 0} + experimentalFeatures={{ newEditingApi: true }} +/> \ No newline at end of file diff --git a/docs/data/data-grid/editing/LinkedFieldsCellEditing.js b/docs/data/data-grid/editing/LinkedFieldsCellEditing.js new file mode 100644 index 00000000000..fecc3be0eb3 --- /dev/null +++ b/docs/data/data-grid/editing/LinkedFieldsCellEditing.js @@ -0,0 +1,144 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import { + DataGrid, + GridEditSingleSelectCell, + GridCellEditStopReasons, +} from '@mui/x-data-grid'; +import { randomPrice } from '@mui/x-data-grid-generator'; + +const initialRows = [ + { + id: 1, + description: 'Light bill', + value: randomPrice(0, 1000), + type: 'Expense', + account: 'Utilities', + }, + { + id: 3, + description: 'Order #5', + value: randomPrice(0, 1000), + type: 'Income', + account: 'Sales', + }, + { + id: 4, + description: 'Google AdSense', + value: randomPrice(0, 1000), + type: 'Income', + account: 'Ads', + }, +]; + +const CustomTypeEditComponent = (props) => { + const { setRows, ...other } = props; + + const handleValueChange = () => { + setRows((prevRows) => { + console.log(prevRows); + return prevRows.map((row) => + row.id === props.id ? { ...row, account: null } : row, + ); + }); + }; + + return ; +}; + +CustomTypeEditComponent.propTypes = { + /** + * The grid row id. + */ + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + setRows: PropTypes.func.isRequired, +}; + +export default function LinkedFieldsCellEditing() { + const editingRow = React.useRef(null); + const [rows, setRows] = React.useState(initialRows); + + const columns = [ + { field: 'description', headerName: 'Description', width: 160, editable: true }, + { + field: 'value', + headerName: 'Value', + type: 'number', + width: 120, + editable: true, + }, + { + field: 'type', + headerName: 'Type', + type: 'singleSelect', + valueOptions: ['Income', 'Expense'], + width: 120, + editable: true, + renderEditCell: (params) => ( + + ), + }, + { + field: 'account', + headerName: 'Account', + type: 'singleSelect', + valueOptions: ({ row }) => { + if (!row) { + return [ + 'Sales', + 'Investments', + 'Ads', + 'Taxes', + 'Payroll', + 'Utilities', + 'Marketing', + ]; + } + + return row.type === 'Income' + ? ['Sales', 'Investments', 'Ads'] + : ['Taxes', 'Payroll', 'Utilities', 'Marketing']; + }, + width: 140, + editable: true, + }, + ]; + + const handleCellEditStart = (params) => { + editingRow.current = rows.find((row) => row.id === params.id); + }; + + const handleCellEditStop = (params) => { + if (params.reason === GridCellEditStopReasons.escapeKeyDown) { + setRows((prevRows) => + prevRows.map((row) => + row.id === editingRow.current.id + ? { ...row, account: editingRow.current.account } + : row, + ), + ); + } + }; + + const processRowUpdate = (newRow) => { + setRows((prevRows) => + prevRows.map((row) => (row.id === editingRow.current.id ? newRow : row)), + ); + + return newRow; + }; + + return ( + + + + ); +} diff --git a/docs/data/data-grid/editing/LinkedFieldsCellEditing.tsx b/docs/data/data-grid/editing/LinkedFieldsCellEditing.tsx new file mode 100644 index 00000000000..eeb35f84160 --- /dev/null +++ b/docs/data/data-grid/editing/LinkedFieldsCellEditing.tsx @@ -0,0 +1,143 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { + DataGrid, + DataGridProps, + GridRowModel, + GridColumns, + GridRowsProp, + GridEditSingleSelectCell, + GridEditSingleSelectCellProps, + GridCellEditStopReasons, +} from '@mui/x-data-grid'; +import { randomPrice } from '@mui/x-data-grid-generator'; + +const initialRows: GridRowsProp = [ + { + id: 1, + description: 'Light bill', + value: randomPrice(0, 1000), + type: 'Expense', + account: 'Utilities', + }, + { + id: 3, + description: 'Order #5', + value: randomPrice(0, 1000), + type: 'Income', + account: 'Sales', + }, + { + id: 4, + description: 'Google AdSense', + value: randomPrice(0, 1000), + type: 'Income', + account: 'Ads', + }, +]; + +interface CustomTypeEditComponentProps extends GridEditSingleSelectCellProps { + setRows: React.Dispatch>; +} + +const CustomTypeEditComponent = (props: CustomTypeEditComponentProps) => { + const { setRows, ...other } = props; + + const handleValueChange = () => { + setRows((prevRows) => { + console.log(prevRows); + return prevRows.map((row) => + row.id === props.id ? { ...row, account: null } : row, + ); + }); + }; + + return ; +}; + +export default function LinkedFieldsCellEditing() { + const editingRow = React.useRef(null); + const [rows, setRows] = React.useState(initialRows); + + const columns: GridColumns = [ + { field: 'description', headerName: 'Description', width: 160, editable: true }, + { + field: 'value', + headerName: 'Value', + type: 'number', + width: 120, + editable: true, + }, + { + field: 'type', + headerName: 'Type', + type: 'singleSelect', + valueOptions: ['Income', 'Expense'], + width: 120, + editable: true, + renderEditCell: (params) => ( + + ), + }, + { + field: 'account', + headerName: 'Account', + type: 'singleSelect', + valueOptions: ({ row }) => { + if (!row) { + return [ + 'Sales', + 'Investments', + 'Ads', + 'Taxes', + 'Payroll', + 'Utilities', + 'Marketing', + ]; + } + + return row.type === 'Income' + ? ['Sales', 'Investments', 'Ads'] + : ['Taxes', 'Payroll', 'Utilities', 'Marketing']; + }, + width: 140, + editable: true, + }, + ]; + + const handleCellEditStart: DataGridProps['onCellEditStart'] = (params) => { + editingRow.current = rows.find((row) => row.id === params.id); + }; + + const handleCellEditStop: DataGridProps['onCellEditStop'] = (params) => { + if (params.reason === GridCellEditStopReasons.escapeKeyDown) { + setRows((prevRows) => + prevRows.map((row) => + row.id === editingRow.current.id + ? { ...row, account: editingRow.current.account } + : row, + ), + ); + } + }; + + const processRowUpdate: DataGridProps['processRowUpdate'] = (newRow) => { + setRows((prevRows) => + prevRows.map((row) => (row.id === editingRow.current.id ? newRow : row)), + ); + return newRow; + }; + + return ( + + + + ); +} diff --git a/docs/data/data-grid/editing/LinkedFieldsCellEditing.tsx.preview b/docs/data/data-grid/editing/LinkedFieldsCellEditing.tsx.preview new file mode 100644 index 00000000000..f7f4eb1ba35 --- /dev/null +++ b/docs/data/data-grid/editing/LinkedFieldsCellEditing.tsx.preview @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/editing/LinkedFieldsRowEditing.js b/docs/data/data-grid/editing/LinkedFieldsRowEditing.js new file mode 100644 index 00000000000..5c9582e508b --- /dev/null +++ b/docs/data/data-grid/editing/LinkedFieldsRowEditing.js @@ -0,0 +1,111 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import { + DataGrid, + GridEditSingleSelectCell, + useGridApiContext, +} from '@mui/x-data-grid'; +import { randomPrice } from '@mui/x-data-grid-generator'; + +const rows = [ + { + id: 1, + description: 'Light bill', + value: randomPrice(0, 1000), + type: 'Expense', + account: 'Utilities', + }, + { + id: 3, + description: 'Order #5', + value: randomPrice(0, 1000), + type: 'Income', + account: 'Sales', + }, + { + id: 4, + description: 'Google AdSense', + value: randomPrice(0, 1000), + type: 'Income', + account: 'Ads', + }, +]; + +const CustomTypeEditComponent = (props) => { + const apiRef = useGridApiContext(); + + const handleValueChange = async () => { + await apiRef.current.setEditCellValue({ + id: props.id, + field: 'account', + value: '', + }); + }; + + return ; +}; + +CustomTypeEditComponent.propTypes = { + /** + * The grid row id. + */ + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, +}; + +export default function LinkedFieldsRowEditing() { + const columns = [ + { field: 'description', headerName: 'Description', width: 160, editable: true }, + { + field: 'value', + headerName: 'Value', + type: 'number', + width: 120, + editable: true, + }, + { + field: 'type', + headerName: 'Type', + type: 'singleSelect', + valueOptions: ['Income', 'Expense'], + width: 120, + editable: true, + renderEditCell: (params) => , + }, + { + field: 'account', + headerName: 'Account', + type: 'singleSelect', + valueOptions: ({ row }) => { + if (!row) { + return [ + 'Sales', + 'Investments', + 'Ads', + 'Taxes', + 'Payroll', + 'Utilities', + 'Marketing', + ]; + } + + return row.type === 'Income' + ? ['Sales', 'Investments', 'Ads'] + : ['Taxes', 'Payroll', 'Utilities', 'Marketing']; + }, + width: 140, + editable: true, + }, + ]; + + return ( + + + + ); +} diff --git a/docs/data/data-grid/editing/LinkedFieldsRowEditing.tsx b/docs/data/data-grid/editing/LinkedFieldsRowEditing.tsx new file mode 100644 index 00000000000..15ba94caa48 --- /dev/null +++ b/docs/data/data-grid/editing/LinkedFieldsRowEditing.tsx @@ -0,0 +1,106 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { + DataGrid, + GridColumns, + GridRowsProp, + GridEditSingleSelectCell, + GridEditSingleSelectCellProps, + useGridApiContext, +} from '@mui/x-data-grid'; +import { randomPrice } from '@mui/x-data-grid-generator'; + +const rows: GridRowsProp = [ + { + id: 1, + description: 'Light bill', + value: randomPrice(0, 1000), + type: 'Expense', + account: 'Utilities', + }, + { + id: 3, + description: 'Order #5', + value: randomPrice(0, 1000), + type: 'Income', + account: 'Sales', + }, + { + id: 4, + description: 'Google AdSense', + value: randomPrice(0, 1000), + type: 'Income', + account: 'Ads', + }, +]; + +const CustomTypeEditComponent = (props: GridEditSingleSelectCellProps) => { + const apiRef = useGridApiContext(); + + const handleValueChange = async () => { + await apiRef.current.setEditCellValue({ + id: props.id, + field: 'account', + value: '', + }); + }; + + return ; +}; + +export default function LinkedFieldsRowEditing() { + const columns: GridColumns = [ + { field: 'description', headerName: 'Description', width: 160, editable: true }, + { + field: 'value', + headerName: 'Value', + type: 'number', + width: 120, + editable: true, + }, + { + field: 'type', + headerName: 'Type', + type: 'singleSelect', + valueOptions: ['Income', 'Expense'], + width: 120, + editable: true, + renderEditCell: (params) => , + }, + { + field: 'account', + headerName: 'Account', + type: 'singleSelect', + valueOptions: ({ row }) => { + if (!row) { + return [ + 'Sales', + 'Investments', + 'Ads', + 'Taxes', + 'Payroll', + 'Utilities', + 'Marketing', + ]; + } + + return row.type === 'Income' + ? ['Sales', 'Investments', 'Ads'] + : ['Taxes', 'Payroll', 'Utilities', 'Marketing']; + }, + width: 140, + editable: true, + }, + ]; + + return ( + + + + ); +} diff --git a/docs/data/data-grid/editing/LinkedFieldsRowEditing.tsx.preview b/docs/data/data-grid/editing/LinkedFieldsRowEditing.tsx.preview new file mode 100644 index 00000000000..deadb947c7e --- /dev/null +++ b/docs/data/data-grid/editing/LinkedFieldsRowEditing.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/editing/ServerSidePersistence.js b/docs/data/data-grid/editing/ServerSidePersistence.js new file mode 100644 index 00000000000..cdb70e31e0d --- /dev/null +++ b/docs/data/data-grid/editing/ServerSidePersistence.js @@ -0,0 +1,126 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; +import Snackbar from '@mui/material/Snackbar'; +import Alert from '@mui/material/Alert'; + +const useFakeMutation = () => { + return React.useCallback( + (user) => + new Promise((resolve, reject) => + setTimeout(() => { + if (user.name?.trim() === '') { + reject(new Error("Error while saving user: name can't be empty.")); + } else { + resolve({ ...user, name: user.name?.toUpperCase() }); + } + }, 200), + ), + [], + ); +}; + +export default function ServerSidePersistence() { + const mutateRow = useFakeMutation(); + + const [snackbar, setSnackbar] = React.useState(null); + + const handleCloseSnackbar = () => setSnackbar(null); + + const processRowUpdate = React.useCallback( + async (newRow) => { + // Make the HTTP request to save in the backend + const response = await mutateRow(newRow); + setSnackbar({ children: 'User successfully saved', severity: 'success' }); + return response; + }, + [mutateRow], + ); + + const handleProcessRowUpdateError = React.useCallback((error) => { + setSnackbar({ children: error.message, severity: 'error' }); + }, []); + + return ( +
+ + {!!snackbar && ( + + + + )} +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing/ServerSidePersistence.tsx b/docs/data/data-grid/editing/ServerSidePersistence.tsx new file mode 100644 index 00000000000..32a00f3daa9 --- /dev/null +++ b/docs/data/data-grid/editing/ServerSidePersistence.tsx @@ -0,0 +1,143 @@ +import * as React from 'react'; +import { + DataGrid, + GridRowModel, + GridColumns, + GridRowId, + GridRowsProp, +} from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; +import Snackbar from '@mui/material/Snackbar'; +import Alert, { AlertProps } from '@mui/material/Alert'; + +interface User { + name: string; + age: number; + id: GridRowId; + dateCreated: Date; + lastLogin: Date; +} + +const useFakeMutation = () => { + return React.useCallback( + (user: Partial) => + new Promise>((resolve, reject) => + setTimeout(() => { + if (user.name?.trim() === '') { + reject(new Error("Error while saving user: name can't be empty.")); + } else { + resolve({ ...user, name: user.name?.toUpperCase() }); + } + }, 200), + ), + [], + ); +}; + +export default function ServerSidePersistence() { + const mutateRow = useFakeMutation(); + + const [snackbar, setSnackbar] = React.useState | null>(null); + + const handleCloseSnackbar = () => setSnackbar(null); + + const processRowUpdate = React.useCallback( + async (newRow: GridRowModel) => { + // Make the HTTP request to save in the backend + const response = await mutateRow(newRow); + setSnackbar({ children: 'User successfully saved', severity: 'success' }); + return response; + }, + [mutateRow], + ); + + const handleProcessRowUpdateError = React.useCallback((error: Error) => { + setSnackbar({ children: error.message, severity: 'error' }); + }, []); + + return ( +
+ + {!!snackbar && ( + + + + )} +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing/StartEditButtonGrid.js b/docs/data/data-grid/editing/StartEditButtonGrid.js new file mode 100644 index 00000000000..086b67f5674 --- /dev/null +++ b/docs/data/data-grid/editing/StartEditButtonGrid.js @@ -0,0 +1,205 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import { DataGrid, GridCellModes } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +function EditToolbar(props) { + const { selectedCellParams, cellMode, cellModesModel, setCellModesModel } = props; + + const handleSaveOrEdit = () => { + if (!selectedCellParams) { + return; + } + const { id, field } = selectedCellParams; + if (cellMode === 'edit') { + setCellModesModel({ + ...cellModesModel, + [id]: { ...cellModesModel[id], [field]: { mode: GridCellModes.View } }, + }); + } else { + setCellModesModel({ + ...cellModesModel, + [id]: { ...cellModesModel[id], [field]: { mode: GridCellModes.Edit } }, + }); + } + }; + + const handleCancel = () => { + if (!selectedCellParams) { + return; + } + const { id, field } = selectedCellParams; + setCellModesModel({ + ...cellModesModel, + [id]: { + ...cellModesModel[id], + [field]: { mode: GridCellModes.View, ignoreModifications: true }, + }, + }); + }; + + const handleMouseDown = (event) => { + // Keep the focus in the cell + event.preventDefault(); + }; + + return ( + + + + + ); +} + +EditToolbar.propTypes = { + cellMode: PropTypes.oneOf(['edit', 'view']).isRequired, + cellModesModel: PropTypes.object.isRequired, + selectedCellParams: PropTypes.shape({ + field: PropTypes.string.isRequired, + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + }), + setCellModesModel: PropTypes.func.isRequired, +}; + +export default function StartEditButtonGrid() { + const [selectedCellParams, setSelectedCellParams] = React.useState(null); + const [cellModesModel, setCellModesModel] = React.useState({}); + + const handleCellFocus = React.useCallback((event) => { + const row = event.currentTarget.parentElement; + const id = row.dataset.id; + const field = event.currentTarget.dataset.field; + setSelectedCellParams({ id, field }); + }, []); + + const cellMode = React.useMemo(() => { + if (!selectedCellParams) { + return 'view'; + } + const { id, field } = selectedCellParams; + return cellModesModel[id]?.[field]?.mode || 'view'; + }, [cellModesModel, selectedCellParams]); + + const handleCellKeyDown = React.useCallback( + (params, event) => { + if (cellMode === 'edit') { + // Prevents calling event.preventDefault() if Tab is pressed on a cell in edit mode + event.defaultMuiPrevented = true; + } + }, + [cellMode], + ); + + return ( +
+ +
+ ); +} + +const columns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing/StartEditButtonGrid.tsx b/docs/data/data-grid/editing/StartEditButtonGrid.tsx new file mode 100644 index 00000000000..75a2cbe9564 --- /dev/null +++ b/docs/data/data-grid/editing/StartEditButtonGrid.tsx @@ -0,0 +1,218 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import { + GridColumns, + GridRowsProp, + DataGrid, + GridRowId, + GridCellModes, + GridEventListener, + GridCellModesModel, +} from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +interface SelectedCellParams { + id: GridRowId; + field: string; +} + +interface EditToolbarProps { + selectedCellParams?: SelectedCellParams; + cellModesModel: GridCellModesModel; + setCellModesModel: (value: GridCellModesModel) => void; + cellMode: 'view' | 'edit'; +} + +function EditToolbar(props: EditToolbarProps) { + const { selectedCellParams, cellMode, cellModesModel, setCellModesModel } = props; + + const handleSaveOrEdit = () => { + if (!selectedCellParams) { + return; + } + const { id, field } = selectedCellParams; + if (cellMode === 'edit') { + setCellModesModel({ + ...cellModesModel, + [id]: { ...cellModesModel[id], [field]: { mode: GridCellModes.View } }, + }); + } else { + setCellModesModel({ + ...cellModesModel, + [id]: { ...cellModesModel[id], [field]: { mode: GridCellModes.Edit } }, + }); + } + }; + + const handleCancel = () => { + if (!selectedCellParams) { + return; + } + const { id, field } = selectedCellParams; + setCellModesModel({ + ...cellModesModel, + [id]: { + ...cellModesModel[id], + [field]: { mode: GridCellModes.View, ignoreModifications: true }, + }, + }); + }; + + const handleMouseDown = (event: React.MouseEvent) => { + // Keep the focus in the cell + event.preventDefault(); + }; + + return ( + + + + + ); +} + +export default function StartEditButtonGrid() { + const [selectedCellParams, setSelectedCellParams] = + React.useState(null); + const [cellModesModel, setCellModesModel] = React.useState({}); + + const handleCellFocus = React.useCallback( + (event: React.FocusEvent) => { + const row = event.currentTarget.parentElement; + const id = row!.dataset.id!; + const field = event.currentTarget.dataset.field!; + setSelectedCellParams({ id, field }); + }, + [], + ); + + const cellMode = React.useMemo(() => { + if (!selectedCellParams) { + return 'view'; + } + const { id, field } = selectedCellParams; + return cellModesModel[id]?.[field]?.mode || 'view'; + }, [cellModesModel, selectedCellParams]); + + const handleCellKeyDown = React.useCallback>( + (params, event) => { + if (cellMode === 'edit') { + // Prevents calling event.preventDefault() if Tab is pressed on a cell in edit mode + event.defaultMuiPrevented = true; + } + }, + [cellMode], + ); + + return ( +
+ +
+ ); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; diff --git a/docs/data/data-grid/editing/ValidateServerNameGrid.js b/docs/data/data-grid/editing/ValidateServerNameGrid.js new file mode 100644 index 00000000000..504ea6353ab --- /dev/null +++ b/docs/data/data-grid/editing/ValidateServerNameGrid.js @@ -0,0 +1,113 @@ +import * as React from 'react'; +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import Tooltip, { tooltipClasses } from '@mui/material/Tooltip'; +import { DataGridPro, GridEditInputCell } from '@mui/x-data-grid-pro'; + +const StyledBox = styled(Box)(({ theme }) => ({ + height: 400, + width: '100%', + '& .MuiDataGrid-cell--editable': { + backgroundColor: theme.palette.mode === 'dark' ? '#376331' : 'rgb(217 243 190)', + '& .MuiInputBase-root': { + height: '100%', + }, + }, + '& .Mui-error': { + backgroundColor: `rgb(126,10,15, ${theme.palette.mode === 'dark' ? 0 : 0.1})`, + color: theme.palette.mode === 'dark' ? '#ff4343' : '#750f0f', + }, +})); + +let promiseTimeout; +function validateName(username) { + const existingUsers = rows.map((row) => row.name.toLowerCase()); + + return new Promise((resolve) => { + promiseTimeout = setTimeout(() => { + const exists = existingUsers.includes(username.toLowerCase()); + resolve(exists ? `${username} is already taken.` : null); + }, Math.random() * 500 + 100); // simulate network latency + }); +} + +const StyledTooltip = styled(({ className, ...props }) => ( + +))(({ theme }) => ({ + [`& .${tooltipClasses.tooltip}`]: { + backgroundColor: theme.palette.error.main, + color: theme.palette.error.contrastText, + }, +})); + +function NameEditInputCell(props) { + const { error } = props; + + return ( + + + + ); +} + +function renderEditName(params) { + return ; +} + +export default function ValidateServerNameGrid() { + const preProcessEditCellProps = async (params) => { + const errorMessage = await validateName(params.props.value.toString()); + return { ...params.props, error: errorMessage }; + }; + + const columns = [ + { + field: 'name', + headerName: 'MUI Contributor', + width: 180, + editable: true, + preProcessEditCellProps, + renderEditCell: renderEditName, + }, + ]; + + React.useEffect(() => { + return () => { + clearTimeout(promiseTimeout); + }; + }, []); + + return ( + + params.row.id === 5} + experimentalFeatures={{ newEditingApi: true }} + /> + + ); +} + +const rows = [ + { + id: 1, + name: 'Damien', + }, + { + id: 2, + name: 'Olivier', + }, + { + id: 3, + name: 'Danail', + }, + { + id: 4, + name: 'Matheus', + }, + { + id: 5, + name: 'You?', + }, +]; diff --git a/docs/data/data-grid/editing/ValidateServerNameGrid.tsx b/docs/data/data-grid/editing/ValidateServerNameGrid.tsx new file mode 100644 index 00000000000..38cc262cc4f --- /dev/null +++ b/docs/data/data-grid/editing/ValidateServerNameGrid.tsx @@ -0,0 +1,120 @@ +import * as React from 'react'; +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import Tooltip, { tooltipClasses, TooltipProps } from '@mui/material/Tooltip'; +import { + GridColumns, + GridRowsProp, + DataGridPro, + GridPreProcessEditCellProps, + GridEditInputCell, + GridRenderEditCellParams, +} from '@mui/x-data-grid-pro'; + +const StyledBox = styled(Box)(({ theme }) => ({ + height: 400, + width: '100%', + '& .MuiDataGrid-cell--editable': { + backgroundColor: theme.palette.mode === 'dark' ? '#376331' : 'rgb(217 243 190)', + '& .MuiInputBase-root': { + height: '100%', + }, + }, + '& .Mui-error': { + backgroundColor: `rgb(126,10,15, ${theme.palette.mode === 'dark' ? 0 : 0.1})`, + color: theme.palette.mode === 'dark' ? '#ff4343' : '#750f0f', + }, +})); + +let promiseTimeout: any; +function validateName(username: string): Promise { + const existingUsers = rows.map((row) => row.name.toLowerCase()); + + return new Promise((resolve) => { + promiseTimeout = setTimeout(() => { + const exists = existingUsers.includes(username.toLowerCase()); + resolve(exists ? `${username} is already taken.` : null); + }, Math.random() * 500 + 100); // simulate network latency + }); +} + +const StyledTooltip = styled(({ className, ...props }: TooltipProps) => ( + +))(({ theme }) => ({ + [`& .${tooltipClasses.tooltip}`]: { + backgroundColor: theme.palette.error.main, + color: theme.palette.error.contrastText, + }, +})); + +function NameEditInputCell(props: GridRenderEditCellParams) { + const { error } = props; + + return ( + + + + ); +} + +function renderEditName(params: GridRenderEditCellParams) { + return ; +} + +export default function ValidateServerNameGrid() { + const preProcessEditCellProps = async (params: GridPreProcessEditCellProps) => { + const errorMessage = await validateName(params.props.value!.toString()); + return { ...params.props, error: errorMessage }; + }; + + const columns: GridColumns = [ + { + field: 'name', + headerName: 'MUI Contributor', + width: 180, + editable: true, + preProcessEditCellProps, + renderEditCell: renderEditName, + }, + ]; + + React.useEffect(() => { + return () => { + clearTimeout(promiseTimeout); + }; + }, []); + + return ( + + params.row.id === 5} + experimentalFeatures={{ newEditingApi: true }} + /> + + ); +} + +const rows: GridRowsProp = [ + { + id: 1, + name: 'Damien', + }, + { + id: 2, + name: 'Olivier', + }, + { + id: 3, + name: 'Danail', + }, + { + id: 4, + name: 'Matheus', + }, + { + id: 5, + name: 'You?', + }, +]; diff --git a/docs/data/data-grid/editing/ValidateServerNameGrid.tsx.preview b/docs/data/data-grid/editing/ValidateServerNameGrid.tsx.preview new file mode 100644 index 00000000000..19870ba81e7 --- /dev/null +++ b/docs/data/data-grid/editing/ValidateServerNameGrid.tsx.preview @@ -0,0 +1,8 @@ + + params.row.id === 5} + experimentalFeatures={{ newEditingApi: true }} + /> + \ No newline at end of file diff --git a/docs/data/data-grid/editing/ValueParserSetterGrid.js b/docs/data/data-grid/editing/ValueParserSetterGrid.js new file mode 100644 index 00000000000..f7686a31037 --- /dev/null +++ b/docs/data/data-grid/editing/ValueParserSetterGrid.js @@ -0,0 +1,49 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +function getFullName(params) { + return `${params.row.firstName || ''} ${params.row.lastName || ''}`; +} + +function setFullName(params) { + const [firstName, lastName] = params.value.toString().split(' '); + return { ...params.row, firstName, lastName }; +} + +function parseFullName(value) { + return String(value) + .split(' ') + .map((str) => (str.length > 0 ? str[0].toUpperCase() + str.slice(1) : '')) + .join(' '); +} + +export default function ValueParserSetterGrid() { + return ( +
+ +
+ ); +} + +const columns = [ + { field: 'firstName', headerName: 'First name', width: 130, editable: true }, + { field: 'lastName', headerName: 'Last name', width: 130, editable: true }, + { + field: 'fullName', + headerName: 'Full name', + width: 160, + editable: true, + valueGetter: getFullName, + valueSetter: setFullName, + valueParser: parseFullName, + sortComparator: (v1, v2) => v1.toString().localeCompare(v2.toString()), + }, +]; + +const defaultRows = [ + { id: 1, lastName: 'Snow', firstName: 'Jon' }, + { id: 2, lastName: 'Lannister', firstName: 'Cersei' }, + { id: 3, lastName: 'Lannister', firstName: 'Jaime' }, + { id: 4, lastName: 'Stark', firstName: 'Arya' }, + { id: 5, lastName: 'Targaryen', firstName: 'Daenerys' }, +]; diff --git a/docs/data/data-grid/editing/ValueParserSetterGrid.tsx b/docs/data/data-grid/editing/ValueParserSetterGrid.tsx new file mode 100644 index 00000000000..9d201f40c3c --- /dev/null +++ b/docs/data/data-grid/editing/ValueParserSetterGrid.tsx @@ -0,0 +1,55 @@ +import * as React from 'react'; +import { + DataGrid, + GridColDef, + GridValueGetterParams, + GridValueSetterParams, + GridCellValue, +} from '@mui/x-data-grid'; + +function getFullName(params: GridValueGetterParams) { + return `${params.row.firstName || ''} ${params.row.lastName || ''}`; +} + +function setFullName(params: GridValueSetterParams) { + const [firstName, lastName] = params.value!.toString().split(' '); + return { ...params.row, firstName, lastName }; +} + +function parseFullName(value: GridCellValue) { + return String(value) + .split(' ') + .map((str) => (str.length > 0 ? str[0].toUpperCase() + str.slice(1) : '')) + .join(' '); +} + +export default function ValueParserSetterGrid() { + return ( +
+ +
+ ); +} + +const columns: GridColDef[] = [ + { field: 'firstName', headerName: 'First name', width: 130, editable: true }, + { field: 'lastName', headerName: 'Last name', width: 130, editable: true }, + { + field: 'fullName', + headerName: 'Full name', + width: 160, + editable: true, + valueGetter: getFullName, + valueSetter: setFullName, + valueParser: parseFullName, + sortComparator: (v1, v2) => v1!.toString().localeCompare(v2!.toString()), + }, +]; + +const defaultRows = [ + { id: 1, lastName: 'Snow', firstName: 'Jon' }, + { id: 2, lastName: 'Lannister', firstName: 'Cersei' }, + { id: 3, lastName: 'Lannister', firstName: 'Jaime' }, + { id: 4, lastName: 'Stark', firstName: 'Arya' }, + { id: 5, lastName: 'Targaryen', firstName: 'Daenerys' }, +]; diff --git a/docs/data/data-grid/editing/ValueParserSetterGrid.tsx.preview b/docs/data/data-grid/editing/ValueParserSetterGrid.tsx.preview new file mode 100644 index 00000000000..ae3bc510d2f --- /dev/null +++ b/docs/data/data-grid/editing/ValueParserSetterGrid.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/editing/editing.md b/docs/data/data-grid/editing/editing.md new file mode 100644 index 00000000000..1ea2982f949 --- /dev/null +++ b/docs/data/data-grid/editing/editing.md @@ -0,0 +1,550 @@ +--- +title: Data Grid - Editing +--- + +# Data Grid - Editing + +

The data grid has built-in support for cell and row editing.

+ +:::warning +This page refers to the new editing API, which is not enabled by default. +To use it, add the following flag: + +```tsx + +``` + +This additional step is required because the default editing API has a couple of issues that can only be fixed with breaking changes, that will only be possible in v6. +To avoid having to wait for the next major release window, all breaking changes needed were included inside this flag. + +If you are looking for the documentation for the default editing API, visit [this page](/x/react-data-grid/editing-legacy/). +Note that it is encouraged to migrate to the new editing API since it will be enabled by default in v6. +Although it says "experimental," you can consider it stable. +::: + +## Making a column editable + +You can make a column editable by enabling the `editable` property in its [column definition](/x/api/data-grid/grid-col-def/): + +```tsx + +``` + +This lets the user edit any cell from the specified column. +By default, only one cell at a time can have its `editMode` prop set to `"edit"`. +To let your users edit all cells in a given row simultaneously, set the `editMode` prop to `"row"`. +For more information, see [the section on row editing](#row-editing). + +The following demo shows an example of how to make all columns editable. +Play with it by double-clicking or pressing Enter in any cell from this column: + +{{"demo": "BasicEditingGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +### Start editing + +Users can start editing a cell (or row if `editMode="row"`) with any of the following actions: + +- Double-clicking a cell +- Pressing Enter, Backspace or Delete—note that the latter two options both delete any existing content +- Pressing any printable key, such as a, E, 0, or $ +- Calling `apiRef.current.startCellEditMode` passing the row ID and column field of the cell to be edited + + ```tsx + apiRef.current.startCellEditMode({ id: 1, field: 'name' }); + ``` + +- Calling `apiRef.current.startRowEditMode` passing the ID of the row (only available if `editMode="row"`). + + ```tsx + apiRef.current.startRowEditMode({ id: 1 }); + ``` + +### Stop editing + +When a cell is in edit mode, the user can stop editing with any of the following interactions: + +- Pressing Escape—this also reverts any changes made +- Pressing Tab—this also saves any changes made +- Pressing Enter—this also saves any changes made and moves the focus to the next cell in the same column +- Clicking outside the cell or row—this also saves any changes made +- Calling `apiRef.current.stopCellEditMode({ id, field })` passing the row ID and column field of the cell that's been edited + + ```tsx + apiRef.current.stopCellEditMode({ id: 1, field: 'name' }); + + // or + + apiRef.current.stopCellEditMode({ + id: 1, + field: 'name', + ignoreModifications: true, // will also discard the changes made + }); + ``` + +- Calling `apiRef.current.stopRowEditMode` passing the ID of the row (only possible if `editMode="row"`). + + ```tsx + apiRef.current.stopRowEditMode({ id: 1 }); + + // or + + apiRef.current.stopRowEditMode({ + id: 1, + ignoreModifications: true, // will also discard the changes made + }); + ``` + +### Disable editing of specific cells within a row + +The `editable` property controls which cells are editable at the column level. +You can use the `isCellEditable` callback prop to define which individual cells the user can edit in a given row. +It is called with a [`GridCellParams`](/x/api/data-grid/grid-cell-params/) object and must return `true` if the cell is editable, or `false` if not. + +In the following demo, only the rows with an even `Age` value are editable. +The editable cells have a green background for better visibility. + +{{"demo": "IsCellEditableGrid.js", "bg": "inline"}} + +## Value parser and value setter + +You can use the `valueParser` property in the column definition to modify the value entered by the user—for example, to convert the value to a different format: + +```tsx +const columns: GridColDef[] = [ + { + valueParser: (value: GridCellValue, params: GridCellParams) => { + return value.toLowerCase(); + }, + }, +]; +``` + +You can use the `valueSetter` property of the column definition to customize how the row is updated with a new value. +This lets you insert a value from a nested object. +It is called with an object containing the new cell value to be saved as well as the row that the cell belongs to. +If you are already using a `valueGetter` to extract the value from a nested object, then the `valueSetter` will probably also be necessary. + +```tsx +const columns: GridColDef[] = [ + { + valueSetter: (params: GridValueSetterParams) => { + const [firstName, lastName] = params.value!.toString().split(' '); + return { ...params.row, firstName, lastName }; + }, + }, +]; +``` + +In the following demo, both the `valueParser` and the `valueSetter` are defined for the **Full name** column. +The `valueParser` capitalizes the value entered, and the `valueSetter` splits the value and saves it correctly into the row model: + +{{"demo": "ValueParserSetterGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +## Events + +The mouse and keyboard interactions that [start](#start-editing) and [stop](#stop-editing) cell editing do so by triggering the `'cellEditStart'` and `'cellEditStop'` [events](/x/react-data-grid/events/), respectively. +For row editing, the events are `'rowEditStart'` and `'rowEditStop'`. +You can control how these events are handled to customize editing behavior. + +For convenience, you can also listen to these events using their respective props: + +- `onCellEditStart` +- `onCellEditStop` +- `onRowEditStart` +- `onRowEditStop` + +These events and props are called with an object containing the row ID and column field of the cell that is being edited. +The object also contains a `reason` param that specifies which type of interaction caused the event to be fired—for instance, `'cellDoubleClick'` when a double-click initiates edit mode. + +The following demo shows how to prevent the user from exiting edit mode when clicking outside of a cell. +To do this, the `onCellEditStop` prop is used to check if the `reason` is `'cellFocusOut'`. +If that condition is true, it [disables](/x/react-data-grid/events/#disabling-the-default-behavior) the default event behavior. +In this context, the user can only stop editing a cell by pressing Enter, Escape or Tab. + +{{"demo": "DisableStopEditModeOnFocusOut.js", "bg": "inline"}} + +## Controlled mode + +Each cell and row has two modes: `edit` and `view`. +You can control the active mode using the props `cellModesModel` and `rowModesModel` (only works if `editMode="row"`). + +The `cellModesModel` prop accepts an object containing the `mode` (and additional options) for a given column field, in a given row, as in the following example. +The options accepted are the same available in [`apiRef.current.startCellEditMode`](#start-editing) and [`apiRef.current.stopCellEditMode`](#stop-editing). + +```tsx +// Changes the mode of field=name from row with id=1 to "edit" + + +// Changes the mode of field=name from row with id=1 to "view", ignoring modifications made + +``` + +For row editing, the `rowModesModel` props work in a similar manner. +The options accepted are the same available in [`apiRef.current.startRowEditMode`](#start-editing) and [`apiRef.current.stopRowEditMode`](#stop-editing). + +```tsx +// Changes the mode of the row with id=1 to "edit" + + +// Changes the mode of the row with id=1 to "view", ignoring modifications made + +``` + +Additionally, the callback props `onCellModesModelChange` and `onRowModesModelChange` (only works if `editMode="row"`) are available. +Use them to update the respective prop. + +In the demo below, `cellModesModel` is used to control the mode of selected cell using the external buttons. +For an example using row editing check the [full-featured CRUD component](#full-featured-crud-component). + +{{"demo": "StartEditButtonGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +:::warning +The options passed to both model props only take effect when `mode` changes. +Updating the params of a cell or row, but keeping the same `mode`, makes the cell or row to stay in the same mode. +Also, removing one field or row ID from the object will not cause the missing cell or row to go to `"view"` mode. +::: + +## Validation + +If the column definition sets a callback for the `preProcessEditCellProps` property, then it will be called each time a new value is entered into a cell from this column. +This property lets you pre-process the props that are passed to the edit component. +The `preProcessEditCellProps` callback is called with an object containing the following attributes: + +- `id`: the row ID +- `row`: the row model containing the value(s) of the cell or row before entering edit mode +- `props`: the props, containing the value after the value parser, that are passed to the edit component +- `hasChanged`: determines if `props.value` is different from the last time this callback was called + +Data validation is one type of pre-processing that can be done in this way. +To validate the data entered, pass a callback to `preProcessEditCellProps` checking if `props.value` is valid. +If the new value is invalid, set `props.error` to a truthy value and return the modified props, as shown in the example below. +When the user tries to save the updated value, the change will be rejected if the error attribute is truthy (invalid). + +```tsx +const columns: GridColDef[] = [ + { + field: 'firstName', + preProcessEditCellProps: (params: GridPreProcessEditCellProps) => { + const hasError = params.props.value.length < 3; + return { ...params.props, error: hasError }; + }, + }, +]; +``` + +:::warning +Changing `props.value` inside the callback has no effect. To pre-process it, use a [value parser](#value-parser-and-value-setter). +::: + +The demo below contains an example of server-side data validation. +In this case, the callback returns a promise that resolves to the modified props. +Note that the value passed to `props.error` is passed directly to the edit component as the `error` prop. +While the promise is not resolved, the edit component will receive an `isProcessingProps` prop with value equal to `true`. + +{{"demo": "ValidateServerNameGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +:::warning +If the user performs an action that saves changes and exits edit mode (e.g. pressing Enter) while the props are still being processed, the changes will be discarded upon exit. +To avoid this, it is important to communicate to users when the processing is still occurring. +You can use the `isProcessingProps` prop to show a loader while waiting for the server to respond. +::: + +## Persistence + +The `processRowUpdate` prop is called when the user performs an action to [stop editing](#stop-editing). +Use this prop to send the new values to the server and save them into a database or other storage method. +The prop is called with two arguments: + +1. the updated row with the new values after passing through the `valueSetter` +2. the values of the row before the cell or row was edited + +Once the row is saved, `processRowUpdate` must return the row object that will be used to update the internal state. +The value returned is used as an argument to a call to `apiRef.current.updateRows`. + +If you need to cancel the save process while calling `processRowUpdate`—for instance, when a database validation fails, or the user wants to reject the changes—there are two options: + +1. Reject the promise so that the internal state is not updated and the cell remains in edit mode +2. Resolve the promise with the second argument—the original value(s)—so that the internal state is not updated, but the cell exits edit mode + +The following demo implements the first option: rejecting the promise. +Instead of [validating](#validation) while typing, it validates in the server. +If the new name is empty, then the promise responsible for saving the row will be rejected and the cell will remain in edit mode. +Additionally, `onProcessRowUpdateError` is called to display the error message. +The demo also shows that `processRowUpdate` can be used to pre-process the row model that will be saved into the internal state. +To exit edit mode, press Escape or enter a valid name. + +{{"demo": "ServerSidePersistence.js", "bg": "inline", "defaultCodeOpen": false}} + +### Ask for confirmation before saving + +The second option—resolving the promise with the second argument—lets the user cancel the save process by rejecting the changes and exiting edit mode. +In this case, `processRowUpdate` is resolved with the second argument—the original value(s) of the cell or row. + +The following demo shows how this approach can be used to ask for confirmation before sending the data to the server. +If the user accepts the change, the internal state is updated with the values. +But if the changes are rejected, the internal state remains unchanged, and the cell is reverted back to its original value. +The demo also employs validation to prevent entering an empty name. + +{{"demo": "AskConfirmationBeforeSave.js", "bg": "inline", "defaultCodeOpen": false}} + +## Create your own edit component + +Each of the built-in column types provides a component to edit the value of the cells. +To customize column types, or override the existing components, you can provide a new edit component through the `renderEditCell` property in the column definition. +This property works like the `renderCell` property, which is rendered while cells are in view mode. + +```tsx +function CustomEditComponent(props: GridRenderEditCellParams) { + return ; +} + +const columns: GridColDef[] = [ + { + field: 'firstName', + renderEditCell: (params: GridRenderEditCellParams) => ( + return ; + ), + }, +]; +``` + +The `renderEditCell` property receives all params from `GridRenderEditCellParams`, which extends `GridCellParams`. +Additionally, the props added during [pre-processing](#validation) are also available in the params. +These are the most important params to consider: + +- `value`: contains the current value of the cell in edit mode, overriding the value from `GridCellParams` +- `error`: the error added during validation +- `isProcessingProps`: whether `preProcessEditCellProps` is being executed or not + +Once a new value is entered into the input, it must be sent to the grid. +To do this, pass the row ID, the column field, and the new cell value to a call to `apiRef.current.setEditCellValue`. +The new value will be parsed and validated, and the `value` prop will reflect the changes in the next render. + +```tsx +function CustomEditComponent(props: GridRenderEditCellParams) { + const { id, value, field } = props; + const apiRef = useGridApiContext(); + + const handleValueChange = (event: React.ChangeEvent) => { + const newValue = event.target.value; // The new value entered by the user + apiRef.current.setEditCellValue({ id, field, value: newValue }); + }; + + return ; +} +``` + +The following demo implements a custom edit component, based on the [`Rating`](https://mui.com/material-ui/react-rating/) component from `@mui/material`, for the **Rating** column. + +{{"demo": "CustomEditComponent.js", "bg": "inline", "defaultCodeOpen": false}} + +### With debounce + +By default, each call to `apiRef.current.setEditCellValue` triggers a new render. +If the edit component requires the user to type a new value, re-rendering the grid too often will drastically reduce performance. +One way to avoid this is to debounce the API calls. +You can use `apiRef.current.setEditCellValue` to handle debouncing by setting the `debounceMs` param to a positive integer that defines a set time period in milliseconds. +No matter how many times the API method is called, the grid will only be re-rendered after that period of time has passed. + +```tsx +apiRef.current.setEditCellValue({ id, field, value: newValue, debounceMs: 200 }); +``` + +When the grid is only set to re-render after a given period of time has passed, the `value` prop will not be updated on each `apiRef.current.setEditCellValue` call. +To avoid a frozen UI, the edit component can keep the current value in an internal state and sync it once `value` changes. +Modify the edit component to enable this feature: + +```diff + function CustomEditComponent(props: GridRenderEditCellParams) { +- const { id, value, field } = props; ++ const { id, value: valueProp, field } = props; ++ const [value, setValue] = React.useState(valueProp); + const apiRef = useGridApiContext(); + + const handleChange = (event: React.ChangeEvent) => { + const newValue = event.target.value; // The new value entered by the user +- apiRef.current.setEditCellValue({ id, field, value: newValue }); ++ apiRef.current.setEditCellValue({ id, field, value: newValue, debounceMs: 200 }); ++ setValue(newValue); + }; + ++ React.useEffect(() => { ++ setValue(valueProp); ++ }, [valueProp]); ++ + return ; +} +``` + +### With auto-stop + +An edit component has "auto-stop" behavior when it stops edit mode as soon as the value is changed. +To picture better, imagine an edit component with a combo, created following the normal [steps](#create-your-own-edit-component). +By default, it would require two clicks to change the value of the cell: one click inside the cell to select a new value, and another click outside the cell to save. +This second click can be avoided if the first click also stops the edit mode. +To create an edit component with auto-stop, call `apiRef.current.stopCellEditMode` after setting the new value. +Since `apiRef.current.setEditCellValue` may do additional processing, you must wait for it to resolve before stopping the edit mode. +Also, it is a good practice to check if `apiRef.current.setEditCellValue` has returned `true`. +It will be `false` if `preProcessEditProps` set an error during [validation](#validation). + +```tsx +const handleChange = async (event: SelectChangeEvent) => { + const isValid = await apiRef.current.setEditCellValue({ + id, + field, + value: event.target.value, + }); + + if (isValid) { + apiRef.current.stopCellEditMode({ id, field }); + } +}; +``` + +The following demo implements an edit component with auto-stop, based on a native [`Select`](/material-ui/react-select/) component for the **Role** column. + +{{"demo": "AutoStopEditComponent.js", "bg": "inline", "defaultCodeOpen": false}} + +:::warning +We don't recommend using edit components with auto-stop in columns that use long-running `preProcessEditCellProps` because the UI will freeze while waiting for `apiRef.current.setEditCellValue`. +Instead, use the provided interactions to exit edit mode. +::: + +## Row editing + +Row editing lets the user edit all cells in a row simultaneously. +The same basic rules for cell editing also apply to row editing. +To enable it, change the `editMode` prop to `"row"`, then follow the same guidelines as those for cell editing to set the `editable` property in the definition of the columns that the user can edit. + +```tsx + +``` + +The following demo illustrates how row editing works. +The user can [start](#start-editing) and [stop](#stop-editing) editing a row using the same actions as those provided for cell editing (e.g. double-clicking a cell). + +{{"demo": "BasicRowEditingGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +:::warning +By design, when changing the value of a cell all `preProcessEditCellProps` callbacks from other columns are also called. +This lets you apply conditional validation where the value of a cell impacts the validation status of another cell in the same row. +If you only want to run validation when the value has changed, check if the `hasChanged` param is `true`. +::: + +### Full-featured CRUD component + +Row editing makes it possible to create a full-featured CRUD (Create, Read, Update, Delete) component similar to those found in enterprise applications. +In the following demo, the typical ways to start and stop editing are all disabled. +Instead, use the buttons available in each row or in the toolbar. + +{{"demo": "FullFeaturedCrudGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +## Advanced use cases + +In the next sections, there examples of how the props provided by editing API can be used to implement complex use cases commonly found in applications. + +### Conditional validation + +When all cells in a row are in edit mode, you can validate fields by comparing their values against one another. +To do this, start by adding the `preProcessEditCellProps` as explained in the [validation](#validation) section. +When the callback is called, it will have an additional `otherFieldsProps` param containing the props from the other fields in the same row. +Use this param to check if the value from the current column is valid or not. +Return the modified `props` containing the error as you would for cell editing. +Once at the least one field has the `error` attribute set to a truthy value, the row will not exit edit mode. + +The following demo requires a value for the **Payment method** column only if the **Is paid?** column is checked: + +{{"demo": "ConditionalValidationGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +### Linked fields + +The options available for one field may depend on the value of another field. +For instance, if the `singleSelect` column is used, you can provide a function to `valueOptions` returning the relevant options for the value selected in another field, as exemplified below. + +```tsx +const columns: GridColDef[] = [ + { + field: 'account', + type: 'singleSelect', + valueOptions: ({ row }) => { + if (!row) { + // The row is not available when filtering this column + return ['Sales', 'Investments', 'Ads', 'Taxes', 'Payroll', 'Utilities']; + } + + return row.type === 'Income' // Gets the value of the "type" field + ? ['Sales', 'Investments', 'Ads'] + : ['Taxes', 'Payroll', 'Utilities']; + }, + }, +]; +``` + +The code above is already enough to display different options in the **Account** column based on the value selected in the **Type** column. +The only task left is to reset the account once the type is changed. +This is needed because the previously selected account will not exist now in the options. +To solve that, you can create a custom edit component, reusing the built-in one, and pass a function to the `onValueChange` prop. +This function should call `apiRef.current.setEditCellValue` to reset the value of the other field. + +```tsx +const CustomTypeEditComponent = (props: GridEditSingleSelectCellProps) => { + const apiRef = useGridApiContext(); + + const handleValueChange = async () => { + await apiRef.current.setEditCellValue({ + id: props.id, + field: 'account', + value: '', + }); + }; + + return ; +}; +``` + +The demo below combines the steps showed above. +You can experiment it by changing the value of any cell in the **Type** column. +The **Account** column is automatically updated with the correct options. + +{{"demo": "LinkedFieldsRowEditing.js", "bg": "inline", "defaultCodeOpen": false}} + +:::warning +The call to `apiRef.current.setEditCellValue` returns a promise that must be awaited. +For instance, if the `singleSelect` column type is used, not awaiting will cause the other column to be rendered with a `value` that is not in the options. + +```ts +const handleChange = async () => { + await apiRef.current.setEditCellValue({ + id: props.id, + field: 'account', + value: '', + }); +}; +``` + +::: + +A similar behavior can be reproduced with cell editing. +Instead of `apiRef.current.setEditCellValue`, the `rows` prop must be updated or `apiRef.current.updateRows` be used. +Note that the `onCellEditStart` and `onCellEditStop` props also have to be used to revert the value of the cell changed, in case the user cancels the edit. + +{{"demo": "LinkedFieldsCellEditing.js", "bg": "inline", "defaultCodeOpen": false}} + +## apiRef [](https://mui.com/store/items/mui-x-pro/) + +{{"demo": "EditApiNoSnap.js", "bg": "inline", "hideToolbar": true}} + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/events/CatalogOfEventsNoSnap.js b/docs/data/data-grid/events/CatalogOfEventsNoSnap.js new file mode 100644 index 00000000000..bf9e93ac654 --- /dev/null +++ b/docs/data/data-grid/events/CatalogOfEventsNoSnap.js @@ -0,0 +1,130 @@ +/* eslint-disable react/no-danger */ +import * as React from 'react'; +import MarkdownElement from 'docs/src/modules/components/MarkdownElement'; +import HighlightedCode from 'docs/src/modules/components/HighlightedCode'; +import Typography from '@mui/material/Typography'; +import Table from '@mui/material/Table'; +import TableHead from '@mui/material/TableHead'; +import TableBody from '@mui/material/TableBody'; +import TableRow from '@mui/material/TableRow'; +import TableCell from '@mui/material/TableCell'; +import Box from '@mui/material/Box'; +import IconButton from '@mui/material/IconButton'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; +import Collapse from '@mui/material/Collapse'; +import events from './events.json'; + +function escapeHTML(value) { + return value.replace(//g, '>'); +} + +const EventRow = ({ event }) => { + const [open, setOpen] = React.useState(false); + + const example = React.useMemo(() => { + const args = ['details, // GridCallbackDetails']; + if (event.event) { + args.unshift(`event, // ${event.event}`); + } + if (event.params) { + args.unshift(`params, // ${event.params}`); + } + + return ` +const onEvent: GridEventListener<'${event.name}'> = ( + ${args.join('\n ')} +) => {...} + +// Imperative subscription +apiRef.current.subscribeEvent( + '${event.name}', + onEvent, +); + +// Hook subscription (only available inside the scope of the grid) +useGridApiEventHandler('${event.name}', onEvent); +`; + }, [event]); + + return ( + + + + setOpen(!open)} + > + {open ? : } + + + + {event.name} + + +
+ {!!event.params && ( +
+ + Params:{' '} + + +
+ )} + {!!event.event && ( +
+ + Event:{' '} + + +
+ )} + + + + + + + + + + + + + ); +}; + +export default function CatalogOfEventsNoSnap() { + return ( + +
+ + + + Name + Description + + + + + {events.map((event) => ( + + ))} + +
+ + ); +} diff --git a/docs/data/data-grid/events/DoubleClickWithCtrlToEdit.js b/docs/data/data-grid/events/DoubleClickWithCtrlToEdit.js new file mode 100644 index 00000000000..07c081a3fad --- /dev/null +++ b/docs/data/data-grid/events/DoubleClickWithCtrlToEdit.js @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function DoubleClickWithCtrlToEdit() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 6, + editable: true, + }); + + return ( +
+ { + if (!event.ctrlKey) { + event.defaultMuiPrevented = true; + } + }} + {...data} + /> +
+ ); +} diff --git a/docs/data/data-grid/events/DoubleClickWithCtrlToEdit.tsx b/docs/data/data-grid/events/DoubleClickWithCtrlToEdit.tsx new file mode 100644 index 00000000000..07c081a3fad --- /dev/null +++ b/docs/data/data-grid/events/DoubleClickWithCtrlToEdit.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function DoubleClickWithCtrlToEdit() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 6, + editable: true, + }); + + return ( +
+ { + if (!event.ctrlKey) { + event.defaultMuiPrevented = true; + } + }} + {...data} + /> +
+ ); +} diff --git a/docs/data/data-grid/events/DoubleClickWithCtrlToEdit.tsx.preview b/docs/data/data-grid/events/DoubleClickWithCtrlToEdit.tsx.preview new file mode 100644 index 00000000000..4fc2e62f016 --- /dev/null +++ b/docs/data/data-grid/events/DoubleClickWithCtrlToEdit.tsx.preview @@ -0,0 +1,8 @@ + { + if (!event.ctrlKey) { + event.defaultMuiPrevented = true; + } + }} + {...data} +/> \ No newline at end of file diff --git a/docs/data/data-grid/events/SubscribeToEvents.js b/docs/data/data-grid/events/SubscribeToEvents.js new file mode 100644 index 00000000000..2cd36a30af3 --- /dev/null +++ b/docs/data/data-grid/events/SubscribeToEvents.js @@ -0,0 +1,33 @@ +import * as React from 'react'; +import { DataGridPro, useGridApiRef } from '@mui/x-data-grid-pro'; +import Alert from '@mui/material/Alert'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function SubscribeToEvents() { + const apiRef = useGridApiRef(); + const [message, setMessage] = React.useState(''); + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 1, + maxColumns: 6, + }); + + React.useEffect(() => { + return apiRef.current.subscribeEvent('columnResize', (params) => { + setMessage(`Column ${params.colDef.headerName} resized to ${params.width}px.`); + }); + }, [apiRef]); + + return ( +
+
+ +
+ {message && ( + + {message} + + )} +
+ ); +} diff --git a/docs/data/data-grid/events/SubscribeToEvents.tsx b/docs/data/data-grid/events/SubscribeToEvents.tsx new file mode 100644 index 00000000000..2bb6d0d0ea7 --- /dev/null +++ b/docs/data/data-grid/events/SubscribeToEvents.tsx @@ -0,0 +1,42 @@ +import * as React from 'react'; +import { + DataGridPro, + GridColumnResizeParams, + useGridApiRef, +} from '@mui/x-data-grid-pro'; +import Alert from '@mui/material/Alert'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function SubscribeToEvents() { + const apiRef = useGridApiRef(); + const [message, setMessage] = React.useState(''); + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 1, + maxColumns: 6, + }); + + React.useEffect(() => { + return apiRef.current.subscribeEvent( + 'columnResize', + (params: GridColumnResizeParams) => { + setMessage( + `Column ${params.colDef.headerName} resized to ${params.width}px.`, + ); + }, + ); + }, [apiRef]); + + return ( +
+
+ +
+ {message && ( + + {message} + + )} +
+ ); +} diff --git a/docs/data/data-grid/events/SubscribeToEvents.tsx.preview b/docs/data/data-grid/events/SubscribeToEvents.tsx.preview new file mode 100644 index 00000000000..84a7e267384 --- /dev/null +++ b/docs/data/data-grid/events/SubscribeToEvents.tsx.preview @@ -0,0 +1,8 @@ +
+ +
+{message && ( + + {message} + +)} \ No newline at end of file diff --git a/docs/data/data-grid/events/events.json b/docs/data/data-grid/events/events.json new file mode 100644 index 00000000000..a130883a42a --- /dev/null +++ b/docs/data/data-grid/events/events.json @@ -0,0 +1,320 @@ +[ + { + "name": "cellClick", + "description": "Fired when a cell is clicked.", + "params": "GridCellParams", + "event": "MuiEvent>" + }, + { + "name": "cellDoubleClick", + "description": "Fired when a cell is double-clicked.", + "params": "GridCellParams", + "event": "MuiEvent>" + }, + { + "name": "cellEditCommit", + "description": "Fired when the props of the edit input are committed.", + "params": "GridCellEditCommitParams", + "event": "MuiEvent" + }, + { + "name": "cellEditStart", + "description": "Fired when the cell turns to edit mode.", + "params": "GridCellEditStartParams", + "event": "MuiEvent | React.KeyboardEvent>" + }, + { + "name": "cellEditStop", + "description": "Fired when the cell turns back to view mode.", + "params": "GridCellEditStopParams", + "event": "MuiEvent" + }, + { + "name": "cellFocusIn", + "description": "Fired when a cell gains focus.", + "params": "GridCellParams", + "event": "MuiEvent<{}>" + }, + { + "name": "cellFocusOut", + "description": "Fired when a cell loses focus.", + "params": "GridCellParams", + "event": "MuiEvent" + }, + { + "name": "cellKeyDown", + "description": "Fired when a keydown event happens in a cell.", + "params": "GridCellParams", + "event": "MuiEvent>" + }, + { + "name": "cellModesModelChange", + "description": "Fired when the model that controls the cell modes changes.", + "params": "GridCellModesModel", + "event": "MuiEvent<{}>" + }, + { + "name": "cellMouseDown", + "description": "Fired when a mousedown event happens in a cell.", + "params": "GridCellParams", + "event": "MuiEvent>" + }, + { + "name": "cellMouseUp", + "description": "Fired when a mouseup event happens in a cell.", + "params": "GridCellParams", + "event": "MuiEvent>" + }, + { + "name": "columnHeaderClick", + "description": "Fired when a column header is clicked", + "params": "GridColumnHeaderParams", + "event": "MuiEvent>" + }, + { + "name": "columnHeaderDoubleClick", + "description": "Fired when a column header is double-clicked.", + "params": "GridColumnHeaderParams", + "event": "MuiEvent>" + }, + { + "name": "columnHeaderKeyDown", + "description": "Fired when a key is pressed in a column header. It's mapped do the keydown DOM event.", + "params": "GridColumnHeaderParams", + "event": "MuiEvent>" + }, + { + "name": "columnOrderChange", + "description": "Fired when the user ends reordering a column.", + "params": "GridColumnOrderChangeParams", + "event": "MuiEvent<{}>" + }, + { + "name": "columnResize", + "description": "Fired during the resizing of a column.", + "params": "GridColumnResizeParams", + "event": "MuiEvent" + }, + { + "name": "columnResizeStart", + "description": "Fired when the user starts resizing a column.", + "params": "{ field: string }", + "event": "MuiEvent>" + }, + { + "name": "columnResizeStop", + "description": "Fired when the user stops resizing a column.", + "params": "null", + "event": "MuiEvent" + }, + { + "name": "columnsChange", + "description": "Fired when the columns state is changed.", + "params": "string[]", + "event": "MuiEvent<{}>" + }, + { + "name": "columnVisibilityChange", + "description": "Fired when a column visibility changes.\nIt is not fired when the columnVisibilityModel is controlled or initialized.\nIt is not fired when toggling all column's visibility at once.", + "params": "GridColumnVisibilityChangeParams", + "event": "MuiEvent<{}>" + }, + { + "name": "columnVisibilityModelChange", + "description": "Fired when the column visibility model changes.", + "params": "GridColumnVisibilityModel", + "event": "MuiEvent<{}>" + }, + { + "name": "columnWidthChange", + "description": "Fired when the width of a column is changed.", + "params": "GridColumnResizeParams", + "event": "MuiEvent" + }, + { + "name": "componentError", + "description": "Fired when an exception is thrown in the grid.", + "params": "any", + "event": "MuiEvent<{}>" + }, + { + "name": "debouncedResize", + "description": "Fired when the grid is resized with a debounced time of 60ms.", + "params": "ElementSize", + "event": "MuiEvent<{}>" + }, + { + "name": "editCellPropsChange", + "description": "Fired when the props of the edit cell changes.", + "params": "GridEditCellPropsParams", + "event": "MuiEvent | {}>" + }, + { + "name": "editRowsModelChange", + "description": "Fired when the row editing model changes.", + "params": "GridEditRowsModel", + "event": "MuiEvent<{}>" + }, + { + "name": "filterModelChange", + "description": "Fired when the filter model changes.", + "params": "GridFilterModel", + "event": "MuiEvent<{}>" + }, + { + "name": "headerSelectionCheckboxChange", + "description": "Fired when the value of the selection checkbox of the header is changed", + "params": "GridHeaderSelectionCheckboxParams", + "event": "MuiEvent<{}>" + }, + { + "name": "menuClose", + "description": "Fired when the grid menu is closed.", + "params": "GridMenuParams", + "event": "MuiEvent<{}>" + }, + { + "name": "menuOpen", + "description": "Fired when the menu is opened.", + "params": "GridMenuParams", + "event": "MuiEvent<{}>" + }, + { + "name": "pageChange", + "description": "Fired when the page changes.", + "params": "number", + "event": "MuiEvent<{}>" + }, + { + "name": "pageSizeChange", + "description": "Fired when the page size changes.", + "params": "number", + "event": "MuiEvent<{}>" + }, + { + "name": "preferencePanelClose", + "description": "Fired when the preference panel is closed.", + "params": "GridPreferencePanelParams", + "event": "MuiEvent<{}>" + }, + { + "name": "preferencePanelOpen", + "description": "Fired when the preference panel is opened.", + "params": "GridPreferencePanelParams", + "event": "MuiEvent<{}>" + }, + { + "name": "resize", + "description": "Fired when the grid is resized.", + "params": "ElementSize", + "event": "MuiEvent<{}>" + }, + { + "name": "rowClick", + "description": "Fired when a row is clicked.\nNot fired if the cell clicked is from an interactive column (actions, checkbox, etc).", + "params": "GridRowParams", + "event": "MuiEvent>" + }, + { + "name": "rowDoubleClick", + "description": "Fired when a row is double-clicked.", + "params": "GridRowParams", + "event": "MuiEvent>" + }, + { + "name": "rowEditCommit", + "description": "Fired when the props of the edit input are committed.", + "params": "GridRowId", + "event": "MuiEvent" + }, + { + "name": "rowEditStart", + "description": "Fired when the row turns to edit mode.", + "params": "GridRowEditStartParams", + "event": "MuiEvent | React.KeyboardEvent>" + }, + { + "name": "rowEditStop", + "description": "Fired when the row turns back to view mode.", + "params": "GridRowEditStopParams", + "event": "MuiEvent" + }, + { + "name": "rowGroupingModelChange", + "description": "Fired when the row grouping model changes.", + "params": "GridRowGroupingModel", + "event": "MuiEvent<{}>" + }, + { + "name": "rowModesModelChange", + "description": "Fired when the model that controls the row modes changes.", + "params": "GridRowModesModel", + "event": "MuiEvent<{}>" + }, + { + "name": "rowMouseEnter", + "description": "Fired when the mouse enters the row. Called with a GridRowParams object.", + "params": "GridRowParams", + "event": "MuiEvent>" + }, + { + "name": "rowMouseLeave", + "description": "Fired when the mouse leaves the row. Called with a GridRowParams object.", + "params": "GridRowParams", + "event": "MuiEvent>" + }, + { + "name": "rowOrderChange", + "description": "Fired when the user ends reordering a row.", + "params": "GridRowOrderChangeParams", + "event": "MuiEvent<{}>" + }, + { + "name": "rowSelectionCheckboxChange", + "description": "Fired when the value of the selection checkbox of a row is changed", + "params": "GridRowSelectionCheckboxParams", + "event": "MuiEvent>" + }, + { + "name": "rowsScroll", + "description": "Fired during the scroll of the grid viewport.", + "params": "GridScrollParams", + "event": "MuiEvent" + }, + { + "name": "rowsScrollEnd", + "description": "Fired when scrolling to the bottom of the grid viewport.", + "params": "GridRowScrollEndParams", + "event": "MuiEvent<{}>" + }, + { + "name": "selectionChange", + "description": "Fired when the selection state of one or multiple rows changes.", + "params": "GridSelectionModel", + "event": "MuiEvent<{}>" + }, + { + "name": "sortModelChange", + "description": "Fired when the sort model changes.", + "params": "GridSortModel", + "event": "MuiEvent<{}>" + }, + { + "name": "stateChange", + "description": "Fired when the state of the grid is updated.", + "params": "any", + "event": "MuiEvent<{}>" + }, + { + "name": "unmount", + "description": "Fired when the grid is unmounted.", + "params": "", + "event": "MuiEvent<{}>" + }, + { + "name": "viewportInnerSizeChange", + "description": "Fired when the inner size of the viewport changes. Called with an ElementSize object.", + "params": "ElementSize", + "event": "MuiEvent<{}>" + } +] diff --git a/docs/data/data-grid/events/events.md b/docs/data/data-grid/events/events.md new file mode 100644 index 00000000000..24c1629d203 --- /dev/null +++ b/docs/data/data-grid/events/events.md @@ -0,0 +1,62 @@ +--- +title: Data Grid - Events +--- + +# Data Grid - Events [](https://mui.com/store/items/mui-x-pro/) + +

The data grid emits events that can be subscribed to attach custom behavior.

+ +## Subscribing to events + +You can subscribe to one of the [events emitted](/x/react-data-grid/events/#catalog-of-events) by calling `apiRef.current.subscribeEvent()` with the name of the event and a handler. The handler will be called with three arguments: + +1. an object with information related to the event +2. a `MuiEvent` containing the DOM event or the React synthetic event, when available +3. a `GridCallbackDetails` containing the `GridApi` only if `DataGridPro` is being used. + +```tsx +/** + * Allows to register a handler for an event. + * @param event The name of event + * @param handler The handler to be called + * @param options Additional options for this listener + * @returns A function to unsubscribe from this event + */ +subscribeEvent: ( + event: GridEventsStr, + handler: (params: any, event: MuiEvent, details: GridCallbackDetails) => void, + options?: EventListenerOptions, +) => () => void; +``` + +The following demo shows how to subscribe to the `columnResize` event. Try it by resizing the columns. + +{{"demo": "SubscribeToEvents.js", "bg": "inline"}} + +## Disabling the default behavior + +Depending on the use case, it might be necessary to disable the default action taken by an event. +The `MuiEvent` passed to the event handler has a `defaultMuiPrevented` property to control when the default behavior can be executed or not. +Set it to `true` to block the default handling of an event and implement your own. + +```tsx +) => { + event.defaultMuiPrevented = true; + }} +/> +``` + +Usually, double clicking a cell will put it into [edit mode](/x/react-data-grid/editing/). +The following example changes this behavior by also requiring Ctrl to be pressed. + +{{"demo": "DoubleClickWithCtrlToEdit.js", "bg": "inline"}} + +## Catalog of events + +{{"demo": "CatalogOfEventsNoSnap.js", "bg": "inline", "hideToolbar": true}} + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/export/CsvExportApiNoSnap.js b/docs/data/data-grid/export/CsvExportApiNoSnap.js new file mode 100644 index 00000000000..6d81354ead2 --- /dev/null +++ b/docs/data/data-grid/export/CsvExportApiNoSnap.js @@ -0,0 +1,7 @@ +import React from 'react'; +import ApiDocs from 'docsx/src/modules/components/ApiDocs'; +import api from 'docsx/pages/x/api/data-grid/grid-csv-export-api.json'; + +export default function CsvExportApiNoSnap() { + return ; +} diff --git a/docs/data/data-grid/export/CsvGetRowsToExport.js b/docs/data/data-grid/export/CsvGetRowsToExport.js new file mode 100644 index 00000000000..615bd20ec03 --- /dev/null +++ b/docs/data/data-grid/export/CsvGetRowsToExport.js @@ -0,0 +1,88 @@ +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Button from '@mui/material/Button'; +import { createSvgIcon } from '@mui/material/utils'; +import { + DataGrid, + gridPaginatedVisibleSortedGridRowIdsSelector, + gridSortedRowIdsSelector, + GridToolbarContainer, + gridVisibleSortedRowIdsSelector, + useGridApiContext, +} from '@mui/x-data-grid'; + +const getRowsFromCurrentPage = ({ apiRef }) => + gridPaginatedVisibleSortedGridRowIdsSelector(apiRef); + +const getUnfilteredRows = ({ apiRef }) => gridSortedRowIdsSelector(apiRef); + +const getFilteredRows = ({ apiRef }) => gridVisibleSortedRowIdsSelector(apiRef); + +const ExportIcon = createSvgIcon( + , + 'SaveAlt', +); + +const CustomToolbar = () => { + const apiRef = useGridApiContext(); + + const handleExport = (options) => apiRef.current.exportDataAsCsv(options); + + const buttonBaseProps = { + color: 'primary', + size: 'small', + startIcon: , + }; + + return ( + + + + + + ); +}; + +export default function CsvGetRowsToExport() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 6, + }); + + return ( +
+ ', value: '20000' }, + ], + }, + }, + }} + /> +
+ ); +} diff --git a/docs/data/data-grid/export/CsvGetRowsToExport.tsx b/docs/data/data-grid/export/CsvGetRowsToExport.tsx new file mode 100644 index 00000000000..25a1c094494 --- /dev/null +++ b/docs/data/data-grid/export/CsvGetRowsToExport.tsx @@ -0,0 +1,93 @@ +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Button, { ButtonProps } from '@mui/material/Button'; +import { createSvgIcon } from '@mui/material/utils'; +import { + DataGrid, + GridCsvExportOptions, + GridCsvGetRowsToExportParams, + gridPaginatedVisibleSortedGridRowIdsSelector, + gridSortedRowIdsSelector, + GridToolbarContainer, + gridVisibleSortedRowIdsSelector, + useGridApiContext, +} from '@mui/x-data-grid'; + +const getRowsFromCurrentPage = ({ apiRef }: GridCsvGetRowsToExportParams) => + gridPaginatedVisibleSortedGridRowIdsSelector(apiRef); + +const getUnfilteredRows = ({ apiRef }: GridCsvGetRowsToExportParams) => + gridSortedRowIdsSelector(apiRef); + +const getFilteredRows = ({ apiRef }: GridCsvGetRowsToExportParams) => + gridVisibleSortedRowIdsSelector(apiRef); + +const ExportIcon = createSvgIcon( + , + 'SaveAlt', +); + +const CustomToolbar = () => { + const apiRef = useGridApiContext(); + + const handleExport = (options: GridCsvExportOptions) => + apiRef.current.exportDataAsCsv(options); + + const buttonBaseProps: ButtonProps = { + color: 'primary', + size: 'small', + startIcon: , + }; + + return ( + + + + + + ); +}; + +export default function CsvGetRowsToExport() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 6, + }); + + return ( +
+ ', value: '20000' }, + ], + }, + }, + }} + /> +
+ ); +} diff --git a/docs/data/data-grid/export/CsvGetRowsToExport.tsx.preview b/docs/data/data-grid/export/CsvGetRowsToExport.tsx.preview new file mode 100644 index 00000000000..72d46e6adfa --- /dev/null +++ b/docs/data/data-grid/export/CsvGetRowsToExport.tsx.preview @@ -0,0 +1,16 @@ +', value: '20000' }, + ], + }, + }, + }} +/> \ No newline at end of file diff --git a/docs/data/data-grid/export/CsvGetRowsToExportRowGrouping.js b/docs/data/data-grid/export/CsvGetRowsToExportRowGrouping.js new file mode 100644 index 00000000000..0a1e901fc2f --- /dev/null +++ b/docs/data/data-grid/export/CsvGetRowsToExportRowGrouping.js @@ -0,0 +1,77 @@ +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Button from '@mui/material/Button'; +import { createSvgIcon } from '@mui/material/utils'; +import { + DataGridPremium, + gridRowTreeSelector, + GridToolbarContainer, + gridFilteredSortedRowIdsSelector, + useGridApiContext, +} from '@mui/x-data-grid-premium'; + +const getRowsWithGroups = ({ apiRef }) => gridFilteredSortedRowIdsSelector(apiRef); + +const getRowsWithoutGroups = ({ apiRef }) => { + const rows = gridFilteredSortedRowIdsSelector(apiRef); + const tree = gridRowTreeSelector(apiRef); + + return rows.filter((rowId) => !tree[rowId].isAutoGenerated); +}; + +const ExportIcon = createSvgIcon( + , + 'SaveAlt', +); + +const CustomToolbar = () => { + const apiRef = useGridApiContext(); + + const handleExport = (options) => apiRef.current.exportDataAsCsv(options); + + const buttonBaseProps = { + color: 'primary', + size: 'small', + startIcon: , + }; + + return ( + + + + + ); +}; + +export default function CsvGetRowsToExportRowGrouping() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/export/CsvGetRowsToExportRowGrouping.tsx b/docs/data/data-grid/export/CsvGetRowsToExportRowGrouping.tsx new file mode 100644 index 00000000000..0c6db54bf78 --- /dev/null +++ b/docs/data/data-grid/export/CsvGetRowsToExportRowGrouping.tsx @@ -0,0 +1,81 @@ +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Button, { ButtonProps } from '@mui/material/Button'; +import { createSvgIcon } from '@mui/material/utils'; +import { + DataGridPremium, + GridCsvExportOptions, + GridCsvGetRowsToExportParams, + gridRowTreeSelector, + GridToolbarContainer, + gridFilteredSortedRowIdsSelector, + useGridApiContext, +} from '@mui/x-data-grid-premium'; + +const getRowsWithGroups = ({ apiRef }: GridCsvGetRowsToExportParams) => + gridFilteredSortedRowIdsSelector(apiRef); + +const getRowsWithoutGroups = ({ apiRef }: GridCsvGetRowsToExportParams) => { + const rows = gridFilteredSortedRowIdsSelector(apiRef); + const tree = gridRowTreeSelector(apiRef); + + return rows.filter((rowId) => !tree[rowId].isAutoGenerated); +}; + +const ExportIcon = createSvgIcon( + , + 'SaveAlt', +); + +const CustomToolbar = () => { + const apiRef = useGridApiContext(); + + const handleExport = (options: GridCsvExportOptions) => + apiRef.current.exportDataAsCsv(options); + + const buttonBaseProps: ButtonProps = { + color: 'primary', + size: 'small', + startIcon: , + }; + + return ( + + + + + ); +}; + +export default function CsvGetRowsToExportRowGrouping() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/export/CsvGetRowsToExportRowGrouping.tsx.preview b/docs/data/data-grid/export/CsvGetRowsToExportRowGrouping.tsx.preview new file mode 100644 index 00000000000..64844802e5f --- /dev/null +++ b/docs/data/data-grid/export/CsvGetRowsToExportRowGrouping.tsx.preview @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/export/CustomExport.js b/docs/data/data-grid/export/CustomExport.js new file mode 100644 index 00000000000..e4e7b5e0b51 --- /dev/null +++ b/docs/data/data-grid/export/CustomExport.js @@ -0,0 +1,107 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { + DataGrid, + GridToolbarContainer, + GridToolbarExportContainer, + GridCsvExportMenuItem, + useGridApiContext, + gridFilteredSortedRowIdsSelector, + gridVisibleColumnFieldsSelector, +} from '@mui/x-data-grid'; +import MenuItem from '@mui/material/MenuItem'; + +const getJson = (apiRef) => { + // Select rows and columns + const filteredSortedRowIds = gridFilteredSortedRowIdsSelector(apiRef); + const visibleColumnsField = gridVisibleColumnFieldsSelector(apiRef); + + // Format the data. Here we only keep the value + const data = filteredSortedRowIds.map((id) => { + const row = {}; + visibleColumnsField.forEach((field) => { + row[field] = apiRef.current.getCellParams(id, field).value; + }); + return row; + }); + + // Stringify with some indentation + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#parameters + return JSON.stringify(data, null, 2); +}; + +const exportBlob = (blob, filename) => { + // Save the blob in a json file + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + + setTimeout(() => { + URL.revokeObjectURL(url); + }); +}; + +const JsonExportMenuItem = (props) => { + const apiRef = useGridApiContext(); + + const { hideMenu } = props; + + return ( + { + const jsonString = getJson(apiRef); + const blob = new Blob([jsonString], { + type: 'text/json', + }); + + exportBlob(blob, 'DataGrid_demo.json'); + + // Hide the export menu after the export + hideMenu?.(); + }} + > + Export JSON + + ); +}; + +JsonExportMenuItem.propTypes = { + hideMenu: PropTypes.func, +}; + +const csvOptions = { delimiter: ';' }; + +const CustomExportButton = (props) => ( + + + + +); + +const CustomToolbar = (props) => ( + + + +); + +export default function CustomExport() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 4, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/export/CustomExport.tsx b/docs/data/data-grid/export/CustomExport.tsx new file mode 100644 index 00000000000..46c4921989c --- /dev/null +++ b/docs/data/data-grid/export/CustomExport.tsx @@ -0,0 +1,106 @@ +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { + DataGrid, + GridToolbarContainer, + GridToolbarContainerProps, + GridToolbarExportContainer, + GridCsvExportMenuItem, + GridCsvExportOptions, + GridExportMenuItemProps, + useGridApiContext, + gridFilteredSortedRowIdsSelector, + gridVisibleColumnFieldsSelector, + GridApi, +} from '@mui/x-data-grid'; +import MenuItem from '@mui/material/MenuItem'; +import { ButtonProps } from '@mui/material/Button'; + +const getJson = (apiRef: React.MutableRefObject) => { + // Select rows and columns + const filteredSortedRowIds = gridFilteredSortedRowIdsSelector(apiRef); + const visibleColumnsField = gridVisibleColumnFieldsSelector(apiRef); + + // Format the data. Here we only keep the value + const data = filteredSortedRowIds.map((id) => { + const row: Record = {}; + visibleColumnsField.forEach((field) => { + row[field] = apiRef.current.getCellParams(id, field).value; + }); + return row; + }); + + // Stringify with some indentation + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#parameters + return JSON.stringify(data, null, 2); +}; + +const exportBlob = (blob: Blob, filename: string) => { + // Save the blob in a json file + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + + setTimeout(() => { + URL.revokeObjectURL(url); + }); +}; + +const JsonExportMenuItem = (props: GridExportMenuItemProps<{}>) => { + const apiRef = useGridApiContext(); + + const { hideMenu } = props; + + return ( + { + const jsonString = getJson(apiRef); + const blob = new Blob([jsonString], { + type: 'text/json', + }); + exportBlob(blob, 'DataGrid_demo.json'); + + // Hide the export menu after the export + hideMenu?.(); + }} + > + Export JSON + + ); +}; + +const csvOptions: GridCsvExportOptions = { delimiter: ';' }; + +const CustomExportButton = (props: ButtonProps) => ( + + + + +); + +const CustomToolbar = (props: GridToolbarContainerProps) => ( + + + +); + +export default function CustomExport() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 4, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/export/CustomExport.tsx.preview b/docs/data/data-grid/export/CustomExport.tsx.preview new file mode 100644 index 00000000000..faffe56d696 --- /dev/null +++ b/docs/data/data-grid/export/CustomExport.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/export/ExcelCustomExport.js b/docs/data/data-grid/export/ExcelCustomExport.js new file mode 100644 index 00000000000..d2bd20c370a --- /dev/null +++ b/docs/data/data-grid/export/ExcelCustomExport.js @@ -0,0 +1,329 @@ +import * as React from 'react'; +import { DataGridPremium, GridToolbar } from '@mui/x-data-grid-premium'; + +const rows = [ + { + id: 1, + path: ['Column', 'Column groups'], + url: '/components/data-grid/columns/#column-groups', + plan: 'Community', + developed: false, + }, + { + id: 2, + path: ['Column', 'Column spanning'], + url: '/components/data-grid/columns/#column-spanning', + plan: 'Community', + developed: false, + }, + { + id: 3, + path: ['Column', 'Column resizing'], + url: '/components/data-grid/columns/#column-resizing', + plan: 'Pro', + developed: true, + }, + { + id: 4, + path: ['Column', 'Column reorder'], + url: '/components/data-grid/columns/#column-reorder', + plan: 'Pro', + developed: true, + }, + { + id: 5, + path: ['Column', 'Column pinning'], + url: '/components/data-grid/columns/#column-pinning', + plan: 'Pro', + developed: true, + }, + { + id: 6, + path: ['Row', 'Row sorting'], + url: '/components/data-grid/rows/#row-sorting', + plan: 'Community', + developed: true, + }, + { + id: 7, + path: ['Row', 'Row height'], + url: '/components/data-grid/rows/#row-height', + plan: 'Community', + developed: true, + }, + { + id: 8, + path: ['Row', 'Row spanning'], + url: '/components/data-grid/rows/#row-spanning', + plan: 'Community', + developed: false, + }, + { + id: 9, + path: ['Row', 'Row reordering'], + url: '/components/data-grid/rows/#row-reorder', + plan: 'Pro', + developed: false, + }, + { + id: 10, + path: ['Row', 'Row pinning'], + url: '/components/data-grid/rows/#row-pinning', + plan: 'Pro', + developed: false, + }, + { + id: 11, + path: ['Selection', 'Single row selection'], + url: '/components/data-grid/selection/#single-row-selection', + plan: 'Community', + developed: true, + }, + { + id: 12, + path: ['Selection', 'Checkbox selection'], + url: '/components/data-grid/selection/#checkbox-selection', + plan: 'Community', + developed: true, + }, + { + id: 13, + path: ['Selection', 'Multiple row selection'], + url: '/components/data-grid/selection/#multiple-row-selection', + plan: 'Pro', + developed: true, + }, + { + id: 14, + path: ['Selection', 'Cell range selection'], + url: '/components/data-grid/selection/#range-selection', + plan: 'Premium', + developed: false, + }, + { + id: 15, + path: ['Filtering', 'Quick filter'], + url: '/components/data-grid/filtering/#quick-filter', + plan: 'Community', + developed: false, + }, + { + id: 16, + path: ['Filtering', 'Column filters'], + url: '/components/data-grid/filtering/#column-filters', + plan: 'Community', + developed: true, + }, + { + id: 17, + path: ['Filtering', 'Multi-column filtering'], + url: '/components/data-grid/filtering/#single-and-multi-filtering', + plan: 'Pro', + developed: true, + }, + { + id: 18, + path: ['Pagination', 'Pagination'], + url: '/components/data-grid/pagination/)', + plan: 'Community', + developed: true, + }, + { + id: 19, + path: ['Pagination', 'Pagination > 100 rows per page'], + url: '/components/data-grid/pagination/#paginate-gt-100-rows', + plan: 'Pro', + developed: true, + }, + { + id: 20, + path: ['Editing', 'Row editing'], + url: '/components/data-grid/editing/#row-editing', + plan: 'Community', + developed: true, + }, + { + id: 21, + path: ['Editing', 'Cell editing'], + url: '/components/data-grid/editing/#cell-editing', + plan: 'Community', + developed: true, + }, + { + id: 22, + path: ['Import & export', 'CSV export'], + url: '/components/data-grid/export/#csv-export', + plan: 'Community', + developed: true, + }, + { + id: 23, + path: ['Import & export', 'Print'], + url: '/components/data-grid/export/#print', + plan: 'Community', + developed: true, + }, + { + id: 24, + path: ['Import & export', 'Clipboard'], + url: '/components/data-grid/export/#clipboard', + plan: 'Pro', + developed: false, + }, + { + id: 25, + path: ['Import & export', 'Excel export'], + url: '/components/data-grid/export/#excel-export', + plan: 'Premium', + developed: true, + }, + { + id: 26, + path: ['Rendering', 'Customizable components'], + url: '/components/data-grid/components/)', + plan: 'Community', + developed: true, + }, + { + id: 27, + path: ['Rendering', 'Column virtualization'], + url: '/components/data-grid/virtualization/#column-virtualization', + plan: 'Community', + developed: true, + }, + { + id: 28, + path: ['Rendering', 'Row virtualization > 100 rows'], + url: '/components/data-grid/virtualization/#row-virtualization', + plan: 'Pro', + developed: true, + }, + { + id: 29, + path: ['Group & Pivot', 'Tree data'], + url: '/components/data-grid/group-pivot/#tree-data', + plan: 'Pro', + developed: true, + }, + { + id: 30, + path: ['Group & Pivot', 'Master detail'], + url: '/components/data-grid/group-pivot/#master-detail', + plan: 'Pro', + developed: false, + }, + { + id: 31, + path: ['Group & Pivot', 'Grouping'], + url: '/components/data-grid/group-pivot/#grouping', + plan: 'Premium', + developed: true, + }, + { + id: 32, + path: ['Group & Pivot', 'Aggregation'], + url: '/components/data-grid/group-pivot/#aggregation', + plan: 'Premium', + developed: false, + }, + { + id: 33, + path: ['Group & Pivot', 'Pivoting'], + url: '/components/data-grid/group-pivot/#pivoting', + plan: 'Premium', + developed: false, + }, + { + id: 34, + path: ['Misc', 'Accessibility'], + url: '/components/data-grid/accessibility/)', + plan: 'Community', + developed: true, + }, + { + id: 35, + path: ['Misc', 'Keyboard navigation'], + url: '/components/data-grid/accessibility/#keyboard-navigation', + plan: 'Community', + developed: true, + }, + { + id: 36, + path: ['Misc', 'Localization'], + url: '/components/data-grid/localization/)', + plan: 'Community', + developed: true, + }, +]; + +const columns = [ + { + field: 'plan', + type: 'singleSelect', + valueOptions: ['Community', 'Pro', 'Premium'], + }, + { + field: 'developed', + type: 'boolean', + }, +]; + +const groupingColDef = { + headerName: 'Feature', +}; + +const exceljsPreProcess = ({ workbook, worksheet }) => { + // Set document meta data + workbook.creator = 'MUI-X team'; + workbook.created = new Date(); + + // Customize default excel properties + worksheet.properties.defaultRowHeight = 30; + + // Create a custom file header + worksheet.mergeCells('A1:C2'); + worksheet.getCell('A1').value = + 'This is an helping document for the MUI-X team.\nPlease refer to the doc for up to date data.'; + + worksheet.getCell('A1').border = { + bottom: { style: 'medium', color: { argb: 'FF007FFF' } }, + }; + + worksheet.getCell('A1').font = { + name: 'Arial Black', + size: 14, + }; + + worksheet.getCell('A1').alignment = { + vertical: 'top', + horizontal: 'center', + wrapText: true, + }; + + worksheet.addRow([]); +}; +const exceljsPostProcess = ({ worksheet }) => { + // add a text after the data + worksheet.addRow({}); // Add empty row + + worksheet.addRow(['Those data are for internal use only']); +}; + +const excelOptions = { exceljsPreProcess, exceljsPostProcess }; + +export default function ExcelCustomExport() { + return ( +
+ row.path} + rows={rows} + columns={columns} + groupingColDef={groupingColDef} + defaultGroupingExpansionDepth={-1} + components={{ Toolbar: GridToolbar }} + componentsProps={{ toolbar: { excelOptions } }} + /> +
+ ); +} diff --git a/docs/data/data-grid/export/ExcelCustomExport.tsx b/docs/data/data-grid/export/ExcelCustomExport.tsx new file mode 100644 index 00000000000..6e2032fa783 --- /dev/null +++ b/docs/data/data-grid/export/ExcelCustomExport.tsx @@ -0,0 +1,332 @@ +import * as React from 'react'; +import { + DataGridPremium, + GridToolbar, + GridExceljsProcessInput, +} from '@mui/x-data-grid-premium'; + +const rows = [ + { + id: 1, + path: ['Column', 'Column groups'], + url: '/components/data-grid/columns/#column-groups', + plan: 'Community', + developed: false, + }, + { + id: 2, + path: ['Column', 'Column spanning'], + url: '/components/data-grid/columns/#column-spanning', + plan: 'Community', + developed: false, + }, + { + id: 3, + path: ['Column', 'Column resizing'], + url: '/components/data-grid/columns/#column-resizing', + plan: 'Pro', + developed: true, + }, + { + id: 4, + path: ['Column', 'Column reorder'], + url: '/components/data-grid/columns/#column-reorder', + plan: 'Pro', + developed: true, + }, + { + id: 5, + path: ['Column', 'Column pinning'], + url: '/components/data-grid/columns/#column-pinning', + plan: 'Pro', + developed: true, + }, + { + id: 6, + path: ['Row', 'Row sorting'], + url: '/components/data-grid/rows/#row-sorting', + plan: 'Community', + developed: true, + }, + { + id: 7, + path: ['Row', 'Row height'], + url: '/components/data-grid/rows/#row-height', + plan: 'Community', + developed: true, + }, + { + id: 8, + path: ['Row', 'Row spanning'], + url: '/components/data-grid/rows/#row-spanning', + plan: 'Community', + developed: false, + }, + { + id: 9, + path: ['Row', 'Row reordering'], + url: '/components/data-grid/rows/#row-reorder', + plan: 'Pro', + developed: false, + }, + { + id: 10, + path: ['Row', 'Row pinning'], + url: '/components/data-grid/rows/#row-pinning', + plan: 'Pro', + developed: false, + }, + + { + id: 11, + path: ['Selection', 'Single row selection'], + url: '/components/data-grid/selection/#single-row-selection', + plan: 'Community', + developed: true, + }, + { + id: 12, + path: ['Selection', 'Checkbox selection'], + url: '/components/data-grid/selection/#checkbox-selection', + plan: 'Community', + developed: true, + }, + { + id: 13, + path: ['Selection', 'Multiple row selection'], + url: '/components/data-grid/selection/#multiple-row-selection', + plan: 'Pro', + developed: true, + }, + { + id: 14, + path: ['Selection', 'Cell range selection'], + url: '/components/data-grid/selection/#range-selection', + plan: 'Premium', + developed: false, + }, + { + id: 15, + path: ['Filtering', 'Quick filter'], + url: '/components/data-grid/filtering/#quick-filter', + plan: 'Community', + developed: false, + }, + { + id: 16, + path: ['Filtering', 'Column filters'], + url: '/components/data-grid/filtering/#column-filters', + plan: 'Community', + developed: true, + }, + { + id: 17, + path: ['Filtering', 'Multi-column filtering'], + url: '/components/data-grid/filtering/#single-and-multi-filtering', + plan: 'Pro', + developed: true, + }, + { + id: 18, + path: ['Pagination', 'Pagination'], + url: '/components/data-grid/pagination/)', + plan: 'Community', + developed: true, + }, + { + id: 19, + path: ['Pagination', 'Pagination > 100 rows per page'], + url: '/components/data-grid/pagination/#paginate-gt-100-rows', + plan: 'Pro', + developed: true, + }, + { + id: 20, + path: ['Editing', 'Row editing'], + url: '/components/data-grid/editing/#row-editing', + plan: 'Community', + developed: true, + }, + { + id: 21, + path: ['Editing', 'Cell editing'], + url: '/components/data-grid/editing/#cell-editing', + plan: 'Community', + developed: true, + }, + { + id: 22, + path: ['Import & export', 'CSV export'], + url: '/components/data-grid/export/#csv-export', + plan: 'Community', + developed: true, + }, + { + id: 23, + path: ['Import & export', 'Print'], + url: '/components/data-grid/export/#print', + plan: 'Community', + developed: true, + }, + { + id: 24, + path: ['Import & export', 'Clipboard'], + url: '/components/data-grid/export/#clipboard', + plan: 'Pro', + developed: false, + }, + { + id: 25, + path: ['Import & export', 'Excel export'], + url: '/components/data-grid/export/#excel-export', + plan: 'Premium', + developed: true, + }, + { + id: 26, + path: ['Rendering', 'Customizable components'], + url: '/components/data-grid/components/)', + plan: 'Community', + developed: true, + }, + { + id: 27, + path: ['Rendering', 'Column virtualization'], + url: '/components/data-grid/virtualization/#column-virtualization', + plan: 'Community', + developed: true, + }, + { + id: 28, + path: ['Rendering', 'Row virtualization > 100 rows'], + url: '/components/data-grid/virtualization/#row-virtualization', + plan: 'Pro', + developed: true, + }, + { + id: 29, + path: ['Group & Pivot', 'Tree data'], + url: '/components/data-grid/group-pivot/#tree-data', + plan: 'Pro', + developed: true, + }, + { + id: 30, + path: ['Group & Pivot', 'Master detail'], + url: '/components/data-grid/group-pivot/#master-detail', + plan: 'Pro', + developed: false, + }, + { + id: 31, + path: ['Group & Pivot', 'Grouping'], + url: '/components/data-grid/group-pivot/#grouping', + plan: 'Premium', + developed: true, + }, + { + id: 32, + path: ['Group & Pivot', 'Aggregation'], + url: '/components/data-grid/group-pivot/#aggregation', + plan: 'Premium', + developed: false, + }, + { + id: 33, + path: ['Group & Pivot', 'Pivoting'], + url: '/components/data-grid/group-pivot/#pivoting', + plan: 'Premium', + developed: false, + }, + { + id: 34, + path: ['Misc', 'Accessibility'], + url: '/components/data-grid/accessibility/)', + plan: 'Community', + developed: true, + }, + { + id: 35, + path: ['Misc', 'Keyboard navigation'], + url: '/components/data-grid/accessibility/#keyboard-navigation', + plan: 'Community', + developed: true, + }, + { + id: 36, + path: ['Misc', 'Localization'], + url: '/components/data-grid/localization/)', + plan: 'Community', + developed: true, + }, +]; + +const columns = [ + { + field: 'plan', + type: 'singleSelect', + valueOptions: ['Community', 'Pro', 'Premium'], + }, + { + field: 'developed', + type: 'boolean', + }, +]; + +const groupingColDef = { + headerName: 'Feature', +}; + +const exceljsPreProcess = ({ workbook, worksheet }: GridExceljsProcessInput) => { + // Set document meta data + workbook.creator = 'MUI-X team'; + workbook.created = new Date(); + + // Customize default excel properties + worksheet.properties.defaultRowHeight = 30; + + // Create a custom file header + worksheet.mergeCells('A1:C2'); + worksheet.getCell('A1').value = + 'This is an helping document for the MUI-X team.\nPlease refer to the doc for up to date data.'; + + worksheet.getCell('A1').border = { + bottom: { style: 'medium', color: { argb: 'FF007FFF' } }, + }; + + worksheet.getCell('A1').font = { + name: 'Arial Black', + size: 14, + }; + worksheet.getCell('A1').alignment = { + vertical: 'top', + horizontal: 'center', + wrapText: true, + }; + worksheet.addRow([]); +}; +const exceljsPostProcess = ({ worksheet }: GridExceljsProcessInput) => { + // add a text after the data + worksheet.addRow({}); // Add empty row + + worksheet.addRow(['Those data are for internal use only']); +}; + +const excelOptions = { exceljsPreProcess, exceljsPostProcess }; + +export default function ExcelCustomExport() { + return ( +
+ row.path} + rows={rows} + columns={columns} + groupingColDef={groupingColDef} + defaultGroupingExpansionDepth={-1} + components={{ Toolbar: GridToolbar }} + componentsProps={{ toolbar: { excelOptions } }} + /> +
+ ); +} diff --git a/docs/data/data-grid/export/ExcelCustomExport.tsx.preview b/docs/data/data-grid/export/ExcelCustomExport.tsx.preview new file mode 100644 index 00000000000..70eb6b0b4b3 --- /dev/null +++ b/docs/data/data-grid/export/ExcelCustomExport.tsx.preview @@ -0,0 +1,10 @@ + row.path} + rows={rows} + columns={columns} + groupingColDef={groupingColDef} + defaultGroupingExpansionDepth={-1} + components={{ Toolbar: GridToolbar }} + componentsProps={{ toolbar: { excelOptions } }} +/> \ No newline at end of file diff --git a/docs/data/data-grid/export/ExcelExport.js b/docs/data/data-grid/export/ExcelExport.js new file mode 100644 index 00000000000..2724229fea4 --- /dev/null +++ b/docs/data/data-grid/export/ExcelExport.js @@ -0,0 +1,138 @@ +import * as React from 'react'; +import { + DataGridPremium, + GridToolbarContainer, + GridToolbarExport, +} from '@mui/x-data-grid-premium'; + +const rows = [ + { + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), + contract: 'full time', + id: 0, + }, + { + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + contract: 'full time', + id: 1, + }, + { + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + contract: 'full time', + id: 2, + }, + { + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + contract: 'part time', + id: 3, + }, + { + jobTitle: 'Sales Person', + recruitmentDate: new Date(2017, 10, 29), + contract: 'part time', + id: 4, + }, + { + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + contract: 'full time', + id: 5, + }, + { + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + contract: 'intern', + id: 6, + }, + { + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + contract: 'full time', + id: 7, + }, + { + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + contract: 'full time', + id: 8, + }, + { + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + contract: 'full time', + id: 9, + }, + { + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + contract: 'full time', + id: 10, + }, + { + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + contract: 'full time', + id: 11, + }, + { + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + contract: 'full time', + id: 12, + }, + { + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + contract: 'intern', + id: 13, + }, + { + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + contract: 'part time', + id: 14, + }, +]; + +const columns = [ + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, + { + field: 'contract', + headerName: 'Contract Type', + type: 'singleSelect', + valueOptions: ['full time', 'part time', 'intern'], + width: 150, + }, +]; + +function CustomToolbar() { + return ( + + + + ); +} + +export default function ExcelExport() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/export/ExcelExport.tsx b/docs/data/data-grid/export/ExcelExport.tsx new file mode 100644 index 00000000000..3e824e9b7be --- /dev/null +++ b/docs/data/data-grid/export/ExcelExport.tsx @@ -0,0 +1,140 @@ +import * as React from 'react'; +import { + DataGridPremium, + GridToolbarContainer, + GridToolbarExport, + GridColumns, + GridRowsProp, +} from '@mui/x-data-grid-premium'; + +const rows: GridRowsProp = [ + { + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), + contract: 'full time', + id: 0, + }, + { + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + contract: 'full time', + id: 1, + }, + { + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + contract: 'full time', + id: 2, + }, + { + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + contract: 'part time', + id: 3, + }, + { + jobTitle: 'Sales Person', + recruitmentDate: new Date(2017, 10, 29), + contract: 'part time', + id: 4, + }, + { + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + contract: 'full time', + id: 5, + }, + { + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + contract: 'intern', + id: 6, + }, + { + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + contract: 'full time', + id: 7, + }, + { + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + contract: 'full time', + id: 8, + }, + { + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + contract: 'full time', + id: 9, + }, + { + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + contract: 'full time', + id: 10, + }, + { + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + contract: 'full time', + id: 11, + }, + { + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + contract: 'full time', + id: 12, + }, + { + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + contract: 'intern', + id: 13, + }, + { + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + contract: 'part time', + id: 14, + }, +]; + +const columns: GridColumns = [ + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, + { + field: 'contract', + headerName: 'Contract Type', + type: 'singleSelect', + valueOptions: ['full time', 'part time', 'intern'], + width: 150, + }, +]; + +function CustomToolbar() { + return ( + + + + ); +} + +export default function ExcelExport() { + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/export/ExcelExport.tsx.preview b/docs/data/data-grid/export/ExcelExport.tsx.preview new file mode 100644 index 00000000000..cb6c984c713 --- /dev/null +++ b/docs/data/data-grid/export/ExcelExport.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/export/ExcelExportApiNoSnap.js b/docs/data/data-grid/export/ExcelExportApiNoSnap.js new file mode 100644 index 00000000000..4d42915cfb3 --- /dev/null +++ b/docs/data/data-grid/export/ExcelExportApiNoSnap.js @@ -0,0 +1,7 @@ +import React from 'react'; +import ApiDocs from 'docsx/src/modules/components/ApiDocs'; +import api from 'docsx/pages/x/api/data-grid/grid-excel-export-api.json'; + +export default function ExcelExportApiNoSnap() { + return ; +} diff --git a/docs/data/data-grid/export/ExportCustomToolbar.js b/docs/data/data-grid/export/ExportCustomToolbar.js new file mode 100644 index 00000000000..1b8d7f4b08e --- /dev/null +++ b/docs/data/data-grid/export/ExportCustomToolbar.js @@ -0,0 +1,31 @@ +import * as React from 'react'; +import { DataGrid, GridToolbarContainer, GridToolbarExport } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function CustomToolbar() { + return ( + + + + ); +} + +export default function ExportCustomToolbar() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 4, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/export/ExportCustomToolbar.tsx b/docs/data/data-grid/export/ExportCustomToolbar.tsx new file mode 100644 index 00000000000..1b8d7f4b08e --- /dev/null +++ b/docs/data/data-grid/export/ExportCustomToolbar.tsx @@ -0,0 +1,31 @@ +import * as React from 'react'; +import { DataGrid, GridToolbarContainer, GridToolbarExport } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function CustomToolbar() { + return ( + + + + ); +} + +export default function ExportCustomToolbar() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 4, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/export/ExportCustomToolbar.tsx.preview b/docs/data/data-grid/export/ExportCustomToolbar.tsx.preview new file mode 100644 index 00000000000..cb09584f9cc --- /dev/null +++ b/docs/data/data-grid/export/ExportCustomToolbar.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/export/ExportDefaultToolbar.js b/docs/data/data-grid/export/ExportDefaultToolbar.js new file mode 100644 index 00000000000..5da3128f705 --- /dev/null +++ b/docs/data/data-grid/export/ExportDefaultToolbar.js @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; + +export default function ExportDefaultToolbar() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 4, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/export/ExportDefaultToolbar.tsx b/docs/data/data-grid/export/ExportDefaultToolbar.tsx new file mode 100644 index 00000000000..5da3128f705 --- /dev/null +++ b/docs/data/data-grid/export/ExportDefaultToolbar.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; + +export default function ExportDefaultToolbar() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 4, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/export/ExportDefaultToolbar.tsx.preview b/docs/data/data-grid/export/ExportDefaultToolbar.tsx.preview new file mode 100644 index 00000000000..5d4baa13f54 --- /dev/null +++ b/docs/data/data-grid/export/ExportDefaultToolbar.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/export/PrintExportApiNoSnap.js b/docs/data/data-grid/export/PrintExportApiNoSnap.js new file mode 100644 index 00000000000..a4632067af5 --- /dev/null +++ b/docs/data/data-grid/export/PrintExportApiNoSnap.js @@ -0,0 +1,7 @@ +import React from 'react'; +import ApiDocs from 'docsx/src/modules/components/ApiDocs'; +import api from 'docsx/pages/x/api/data-grid/grid-print-export-api.json'; + +export default function PrintExportApiNoSnap() { + return ; +} diff --git a/docs/data/data-grid/export/RemovePrintExport.js b/docs/data/data-grid/export/RemovePrintExport.js new file mode 100644 index 00000000000..150eef80d3e --- /dev/null +++ b/docs/data/data-grid/export/RemovePrintExport.js @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { DataGrid, GridToolbarContainer, GridToolbarExport } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function CustomToolbar() { + return ( + + + + ); +} +export default function RemovePrintExport() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 4, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/export/RemovePrintExport.tsx b/docs/data/data-grid/export/RemovePrintExport.tsx new file mode 100644 index 00000000000..150eef80d3e --- /dev/null +++ b/docs/data/data-grid/export/RemovePrintExport.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { DataGrid, GridToolbarContainer, GridToolbarExport } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function CustomToolbar() { + return ( + + + + ); +} +export default function RemovePrintExport() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 4, + maxColumns: 6, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/export/RemovePrintExport.tsx.preview b/docs/data/data-grid/export/RemovePrintExport.tsx.preview new file mode 100644 index 00000000000..cb09584f9cc --- /dev/null +++ b/docs/data/data-grid/export/RemovePrintExport.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/export/export.md b/docs/data/data-grid/export/export.md new file mode 100644 index 00000000000..3727417c111 --- /dev/null +++ b/docs/data/data-grid/export/export.md @@ -0,0 +1,356 @@ +--- +title: Data Grid - Export +--- + +# Data Grid - Export + +

Easily export the rows in various file formats such as CSV, Excel, or PDF.

+ +## Enabling export + +### Default Toolbar + +To enable the export menu, pass the `GridToolbar` component in the `Toolbar` [component slot](/x/react-data-grid/components/#toolbar). + +{{"demo": "ExportDefaultToolbar.js", "bg": "inline"}} + +### Custom Toolbar + +The export menu is provided in a stand-alone component named `GridToolbarExport`. You can use it in a custom toolbar component as follows. + +```jsx +function CustomToolbar() { + return ( + + + + ); +} +``` + +{{"demo": "ExportCustomToolbar.js", "bg": "inline", "defaultCodeOpen": false}} + +## Export options + +By default, the export menu displays all the available export formats, according to your license, which are + +- [Print](#print-export) +- [CSV](#csv-export) +- [Excel](#excel-export) [](https://mui.com/store/items/mui-x-premium/) +- [Clipboard](#clipboard) [](https://mui.com/store/items/mui-x-premium/) (🚧 Not delivered yet) + +You can customize their respective behavior by passing an options object either to the `GridToolbar` or to the `GridToolbarExport` as a prop. + +```tsx + +// same as + +``` + +Each export option has its own API page: + +- [`csvOptions`](/x/api/data-grid/grid-csv-export-options/) +- [`printOptions`](/x/api/data-grid/grid-print-export-options/) + +## Disabled format + +You can remove an export format from the toolbar by setting its option property `disableToolbarButton` to `true`. +In the following example, the print export is disabled. + +```jsx + +``` + +{{"demo": "RemovePrintExport.js", "bg": "inline", "defaultCodeOpen": false}} + +## Exported columns + +By default, the export will only contain the visible columns of the grid. +There are a few ways to include or hide other columns. + +- Set the `disableExport` attribute to `true` in `GridColDef` for columns you don't want to be exported. + +```jsx + +``` + +- Set `allColumns` in export option to `true` to also include hidden columns. Those with `disableExport=true` will not be exported. + +```jsx + +``` + +- Set the exact columns to be exported in the export option. Setting `fields` overrides the other properties. Such that the exported columns are exactly those in `fields` in the same order. + +```jsx + +``` + +## Exported rows + +:::warning +This section only applies to the CSV and the Excel export. +The print export always prints rows in their current state. +::: + +By default, the grid exports the selected rows if there are any. +If not, it exports all rows (filtered and sorted rows, according to active rules), including the collapsed ones. + +Alternatively, you can set the `getRowsToExport` function and export any rows you want, as in the following example. +The grid exports a few [selectors](/x/react-data-grid/state/#access-the-state) that can help you get the rows for the most common use-cases: + +| Selector | Behavior | +| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `gridRowIdsSelector` | The rows in their original order. | +| `gridSortedRowIdsSelector` | The rows after applying the sorting rules. | +| `gridFilteredSortedRowIdsSelector` | The rows after applying the sorting rules, and the filtering rules. | +| `gridVisibleSortedRowIdsSelector` | The rows after applying the sorting rules, the filtering rules, and without the collapsed rows. | +| `gridPaginatedVisibleSortedGridRowIdsSelector` | The rows after applying the sorting rules, the filtering rules, without the collapsed rows and only for the current page (**Note**: If the pagination is disabled, it will still take the value of `page` and `pageSize`). | + +{{"demo": "CsvGetRowsToExport.js", "bg": "inline", "defaultCodeOpen": false}} + +When using [Row grouping](/x/react-data-grid/row-grouping/), it can be useful to remove the groups from the CSV export. + +{{"demo": "CsvGetRowsToExportRowGrouping.js", "bg": "inline", "defaultCodeOpen": false}} + +## CSV export + +### Exported cells + +When the value of a field is an object or a `renderCell` is provided, the CSV export might not display the value correctly. +You can provide a [`valueFormatter`](/x/react-data-grid/column-definition/#value-formatter) with a string representation to be used. + +```jsx + `${value * 100}%`, + renderCell: ({ value }) => , + }, + ]} +/> +``` + +### File encoding + +You can use `csvOptions` to specify the format of the export, such as the `delimiter` character used to separate fields, the `fileName`, or `utf8WithBom` to prefix the exported file with UTF-8 Byte Order Mark (BOM). +For more details on these options, please visit the [`csvOptions` api page](/x/api/data-grid/grid-csv-export-options/). + +```jsx + +``` + +## Print export + +### Modify the grid style + +By default, the printed grid is equivalent to printing a page containing only the grid. +To modify the styles used for printing, such as colors, you can either use the `@media print` media query or the `pageStyle` property of `printOptions`. + +For example, if the grid is in dark mode, the text color will be inappropriate for printing (too light). + +With media query, you have to start your `sx` object with `@media print` key, such that all the style inside are only applied when printing. + +```jsx + +``` + +With `pageStyle` option, you can override the main content color with a [more specific selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity). + +```jsx + +``` + +### Customize grid display + +By default, the print export display all the DataGrid. It is possible to remove the footer and the toolbar by setting respectively `hideFooter` and `hideToolbar` to `true`. + +```jsx + +``` + +For more option to customize the print export, please visit the [`printOptions` api page](/x/api/data-grid/grid-print-export-options/). + +:::warning +Due to the fact that the Print export relies on the usage of an `iframe`, there is a limitation around the usage of `X-Frame-Options`. + +In order for the Print export to work as expected set `X-Frame-Options: SAMEORIGIN`. +::: + +## Custom export format + +You can add custom export formats by creating your own export menu. +To simplify its creation, we export `` which contains the menu logic. +The default `` is defined as follow: + +```jsx +const GridToolbarExport = ({ csvOptions, printOptions, ...other }) => ( + + + + +); +``` + +Each child of the `` receives a prop `hideMenu` to close the export menu after the export. +The demo below shows how to add a JSON export. + +{{"demo": "CustomExport.js", "bg": "inline", "defaultCodeOpen": false}} + +## Excel export [](https://mui.com/store/items/mui-x-premium/) + +This feature relies on [exceljs](https://github.com/exceljs/exceljs). +To install it: + +```sh + // with npm + npm install exceljs + + // with yarn + yarn add exceljs +``` + +The Excel export allows translating columns' type and tree structure of a DataGrid to an Excel file. + +Columns with types `'boolean'`, `'number'`, `'singleSelect'`, `'date'`, and `'dateTime'` are exported in their corresponding type in Excel. Please ensure the `rows` values have the correct type, you can always [convert them](/components/data-grid/columns/#converting-types) as needed. + +{{"demo": "ExcelExport.js", "bg": "inline", "defaultCodeOpen": false}} + +### Customization + +#### Customizing the columns + +You can use the property `columnsStyles` to customize the column style. +This property accepts an object in which keys are the column field and values an [exceljs style object](https://github.com/exceljs/exceljs#styles). + +This can be used to specify value formatting or to add some colors. + +```jsx + +``` + +#### Customizing the document + +You can customize the document using two callback functions: + +- `exceljsPreProcess` called **before** adding the rows' dataset. +- `exceljsPostProcess` called **after** the dataset has been exported to the document. + +Both functions receive `{ workbook, worksheet }` as input. +They are [exceljs](https://github.com/exceljs/exceljs#interface) objects and allow you to directly manipulate the Excel file. + +Thanks to these two methods, you can modify the metadata of the exported spreadsheet. +You can also use it to add custom content on top or bottom of the worksheet, as follows: + +```jsx +function exceljsPreProcess({ workbook, worksheet }) { + workbook.created = new Date(); // Add metadata + worksheet.name = 'Monthly Results'; // Modify worksheet name + + // Write on first line the date of creation + worksheet.getCell('A1').value = `Values from the`; + worksheet.getCell('A2').value = new Date(); +} + +function exceljsPostProcess({ worksheet }) { + // Add a text after the data + worksheet.addRow(); // Add empty row + + const newRow = worksheet.addRow(); + newRow.getCell(1).value = 'Those data are for internal use only'; +} + +// ... + +; +``` + +Since `exceljsPreProcess` is applied before adding the content of the grid, you can use it to add some informative rows on top of the document. +The content of the grid will start on the next row after those added by `exceljsPreProcess`. + +To customize the rows after the grid content, you should use `exceljsPostProcess`. As it is applied after adding the content, you can also use it to access the generated cells. + +In the following demo, both methods are used to set a custom header and a custom footer. + +{{"demo": "ExcelCustomExport.js", "bg": "inline", "defaultCodeOpen": false}} + +## 🚧 Clipboard [](https://mui.com/store/items/mui-x-premium/) + +:::warning +This feature isn't implemented yet. It's coming. + +👍 Upvote [issue #199](https://github.com/mui/mui-x/issues/199) if you want to see it land faster. +You will be able to copy and paste items to and from the grid using the system clipboard. +::: + +## apiRef [](https://mui.com/store/items/mui-x-pro/) + +:::warning +Only use this API as the last option. Give preference to the props to control the grid. +::: + +### CSV + +{{"demo": "CsvExportApiNoSnap.js", "bg": "inline", "hideToolbar": true}} + +### Print + +{{"demo": "PrintExportApiNoSnap.js", "bg": "inline", "hideToolbar": true}} + +### Excel [](https://mui.com/store/items/mui-x-premium/) + +{{"demo": "ExcelExportApiNoSnap.js", "bg": "inline", "hideToolbar": true}} + +## API + +- [csvOptions](/x/api/data-grid/grid-csv-export-options/) +- [printOptions](/x/api/data-grid/grid-print-export-options/) +- [excelOptions](/x/api/data-grid/grid-excel-export-options/) +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) diff --git a/docs/data/data-grid/filtering/BasicExampleDataGrid.js b/docs/data/data-grid/filtering/BasicExampleDataGrid.js new file mode 100644 index 00000000000..02fc59c6d27 --- /dev/null +++ b/docs/data/data-grid/filtering/BasicExampleDataGrid.js @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function BasicExampleDataGrid() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/BasicExampleDataGrid.tsx b/docs/data/data-grid/filtering/BasicExampleDataGrid.tsx new file mode 100644 index 00000000000..02fc59c6d27 --- /dev/null +++ b/docs/data/data-grid/filtering/BasicExampleDataGrid.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function BasicExampleDataGrid() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/BasicExampleDataGrid.tsx.preview b/docs/data/data-grid/filtering/BasicExampleDataGrid.tsx.preview new file mode 100644 index 00000000000..7ffc508f252 --- /dev/null +++ b/docs/data/data-grid/filtering/BasicExampleDataGrid.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/filtering/BasicExampleDataGridPro.js b/docs/data/data-grid/filtering/BasicExampleDataGridPro.js new file mode 100644 index 00000000000..c780f8c9dac --- /dev/null +++ b/docs/data/data-grid/filtering/BasicExampleDataGridPro.js @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function BasicExampleDataGridPro() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/BasicExampleDataGridPro.tsx b/docs/data/data-grid/filtering/BasicExampleDataGridPro.tsx new file mode 100644 index 00000000000..c780f8c9dac --- /dev/null +++ b/docs/data/data-grid/filtering/BasicExampleDataGridPro.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function BasicExampleDataGridPro() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/BasicExampleDataGridPro.tsx.preview b/docs/data/data-grid/filtering/BasicExampleDataGridPro.tsx.preview new file mode 100644 index 00000000000..0f1aa8c3315 --- /dev/null +++ b/docs/data/data-grid/filtering/BasicExampleDataGridPro.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/filtering/ControlledFilters.js b/docs/data/data-grid/filtering/ControlledFilters.js new file mode 100644 index 00000000000..9b8378defaf --- /dev/null +++ b/docs/data/data-grid/filtering/ControlledFilters.js @@ -0,0 +1,36 @@ +import * as React from 'react'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function ControlledFilters() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + const [filterModel, setFilterModel] = React.useState({ + items: [ + { + columnField: 'rating', + operatorValue: '>', + value: '2.5', + }, + ], + }); + + return ( +
+ setFilterModel(newFilterModel)} + /> +
+ ); +} diff --git a/docs/data/data-grid/filtering/ControlledFilters.tsx b/docs/data/data-grid/filtering/ControlledFilters.tsx new file mode 100644 index 00000000000..34012a02988 --- /dev/null +++ b/docs/data/data-grid/filtering/ControlledFilters.tsx @@ -0,0 +1,36 @@ +import * as React from 'react'; +import { DataGrid, GridFilterModel, GridToolbar } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function ControlledFilters() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + const [filterModel, setFilterModel] = React.useState({ + items: [ + { + columnField: 'rating', + operatorValue: '>', + value: '2.5', + }, + ], + }); + + return ( +
+ setFilterModel(newFilterModel)} + /> +
+ ); +} diff --git a/docs/data/data-grid/filtering/ControlledFilters.tsx.preview b/docs/data/data-grid/filtering/ControlledFilters.tsx.preview new file mode 100644 index 00000000000..36453032ff0 --- /dev/null +++ b/docs/data/data-grid/filtering/ControlledFilters.tsx.preview @@ -0,0 +1,8 @@ + setFilterModel(newFilterModel)} +/> \ No newline at end of file diff --git a/docs/data/data-grid/filtering/CustomFilterPanelContent.js b/docs/data/data-grid/filtering/CustomFilterPanelContent.js new file mode 100644 index 00000000000..590515f382b --- /dev/null +++ b/docs/data/data-grid/filtering/CustomFilterPanelContent.js @@ -0,0 +1,96 @@ +import * as React from 'react'; +import { DataGridPro, GridLinkOperator, GridToolbar } from '@mui/x-data-grid-pro'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +const initialState = { + filter: { + filterModel: { + items: [ + { + id: 1, + columnField: 'name', + operatorValue: 'contains', + value: 'D', + }, + { + id: 2, + columnField: 'name', + operatorValue: 'contains', + value: 'D', + }, + { + id: 3, + columnField: 'rating', + operatorValue: '>', + value: '0', + }, + ], + }, + }, +}; + +export default function CustomFilterPanelContent() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + return ( +
+ + theme.palette.mode === 'dark' ? '#444' : '#f5f5f5', + }, + '& .MuiDataGrid-filterFormLinkOperatorInput': { mr: 2 }, + '& .MuiDataGrid-filterFormColumnInput': { mr: 2, width: 150 }, + '& .MuiDataGrid-filterFormOperatorInput': { mr: 2 }, + '& .MuiDataGrid-filterFormValueInput': { width: 200 }, + }, + }, + }} + initialState={initialState} + /> +
+ ); +} diff --git a/docs/data/data-grid/filtering/CustomFilterPanelContent.tsx b/docs/data/data-grid/filtering/CustomFilterPanelContent.tsx new file mode 100644 index 00000000000..12ceccce16a --- /dev/null +++ b/docs/data/data-grid/filtering/CustomFilterPanelContent.tsx @@ -0,0 +1,97 @@ +import * as React from 'react'; +import { DataGridPro, GridLinkOperator, GridToolbar } from '@mui/x-data-grid-pro'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import type { Theme } from '@mui/material/styles'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +const initialState = { + filter: { + filterModel: { + items: [ + { + id: 1, + columnField: 'name', + operatorValue: 'contains', + value: 'D', + }, + { + id: 2, + columnField: 'name', + operatorValue: 'contains', + value: 'D', + }, + { + id: 3, + columnField: 'rating', + operatorValue: '>', + value: '0', + }, + ], + }, + }, +}; + +export default function CustomFilterPanelContent() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + return ( +
+ + theme.palette.mode === 'dark' ? '#444' : '#f5f5f5', + }, + '& .MuiDataGrid-filterFormLinkOperatorInput': { mr: 2 }, + '& .MuiDataGrid-filterFormColumnInput': { mr: 2, width: 150 }, + '& .MuiDataGrid-filterFormOperatorInput': { mr: 2 }, + '& .MuiDataGrid-filterFormValueInput': { width: 200 }, + }, + }, + }} + initialState={initialState} + /> +
+ ); +} diff --git a/docs/data/data-grid/filtering/CustomFilterPanelPosition.js b/docs/data/data-grid/filtering/CustomFilterPanelPosition.js new file mode 100644 index 00000000000..3133baeee60 --- /dev/null +++ b/docs/data/data-grid/filtering/CustomFilterPanelPosition.js @@ -0,0 +1,49 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { + DataGrid, + GridToolbarContainer, + GridToolbarFilterButton, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +const CustomToolbar = ({ setFilterButtonEl }) => ( + + + +); + +CustomToolbar.propTypes = { + setFilterButtonEl: PropTypes.func.isRequired, +}; + +export default function CustomFilterPanelPosition() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + const [filterButtonEl, setFilterButtonEl] = React.useState(null); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/CustomFilterPanelPosition.tsx b/docs/data/data-grid/filtering/CustomFilterPanelPosition.tsx new file mode 100644 index 00000000000..2aea5193f42 --- /dev/null +++ b/docs/data/data-grid/filtering/CustomFilterPanelPosition.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; +import { + DataGrid, + GridToolbarContainer, + GridToolbarFilterButton, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +const CustomToolbar: React.FunctionComponent<{ + setFilterButtonEl: React.Dispatch>; +}> = ({ setFilterButtonEl }) => ( + + + +); + +export default function CustomFilterPanelPosition() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + const [filterButtonEl, setFilterButtonEl] = + React.useState(null); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/CustomFilterPanelPosition.tsx.preview b/docs/data/data-grid/filtering/CustomFilterPanelPosition.tsx.preview new file mode 100644 index 00000000000..063ebc3c5de --- /dev/null +++ b/docs/data/data-grid/filtering/CustomFilterPanelPosition.tsx.preview @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/filtering/CustomInputComponent.js b/docs/data/data-grid/filtering/CustomInputComponent.js new file mode 100644 index 00000000000..40a77db57d7 --- /dev/null +++ b/docs/data/data-grid/filtering/CustomInputComponent.js @@ -0,0 +1,125 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import Rating from '@mui/material/Rating'; +import { DataGrid, getGridNumericOperators } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function RatingInputValue(props) { + const { item, applyValue, focusElementRef } = props; + + const ratingRef = React.useRef(null); + React.useImperativeHandle(focusElementRef, () => ({ + focus: () => { + ratingRef.current + .querySelector(`input[value="${Number(item.value) || ''}"]`) + .focus(); + }, + })); + + const handleFilterChange = (event, newValue) => { + applyValue({ ...item, value: newValue }); + }; + + return ( + + + + ); +} + +RatingInputValue.propTypes = { + applyValue: PropTypes.func.isRequired, + focusElementRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ + current: PropTypes.any.isRequired, + }), + ]), + item: PropTypes.shape({ + /** + * The column from which we want to filter the rows. + */ + columnField: PropTypes.string.isRequired, + /** + * Must be unique. + * Only useful when the model contains several items. + */ + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + /** + * The name of the operator we want to apply. + * Will become required on `@mui/x-data-grid@6.X`. + */ + operatorValue: PropTypes.string, + /** + * The filtering value. + * The operator filtering function will decide for each row if the row values is correct compared to this value. + */ + value: PropTypes.any, + }).isRequired, +}; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function CustomInputComponent() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + const columns = React.useMemo( + () => + data.columns.map((col) => { + if (col.field === 'rating') { + return { + ...col, + filterOperators: getGridNumericOperators() + .filter((operator) => operator.value !== 'isAnyOf') + .map((operator) => ({ + ...operator, + InputComponent: operator.InputComponent + ? RatingInputValue + : undefined, + })), + }; + } + return col; + }), + [data.columns], + ); + + return ( +
+ =' }, + ], + }, + }, + }} + /> +
+ ); +} diff --git a/docs/data/data-grid/filtering/CustomInputComponent.tsx b/docs/data/data-grid/filtering/CustomInputComponent.tsx new file mode 100644 index 00000000000..e31ee7b292a --- /dev/null +++ b/docs/data/data-grid/filtering/CustomInputComponent.tsx @@ -0,0 +1,97 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Rating, { RatingProps } from '@mui/material/Rating'; +import { + DataGrid, + GridFilterInputValueProps, + getGridNumericOperators, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function RatingInputValue(props: GridFilterInputValueProps) { + const { item, applyValue, focusElementRef } = props; + + const ratingRef: React.Ref = React.useRef(null); + React.useImperativeHandle(focusElementRef, () => ({ + focus: () => { + ratingRef.current + .querySelector(`input[value="${Number(item.value) || ''}"]`) + .focus(); + }, + })); + + const handleFilterChange: RatingProps['onChange'] = (event, newValue) => { + applyValue({ ...item, value: newValue }); + }; + + return ( + + + + ); +} + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function CustomInputComponent() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + const columns = React.useMemo( + () => + data.columns.map((col) => { + if (col.field === 'rating') { + return { + ...col, + filterOperators: getGridNumericOperators() + .filter((operator) => operator.value !== 'isAnyOf') + .map((operator) => ({ + ...operator, + InputComponent: operator.InputComponent + ? RatingInputValue + : undefined, + })), + }; + } + return col; + }), + [data.columns], + ); + + return ( +
+ =' }, + ], + }, + }, + }} + /> +
+ ); +} diff --git a/docs/data/data-grid/filtering/CustomInputComponent.tsx.preview b/docs/data/data-grid/filtering/CustomInputComponent.tsx.preview new file mode 100644 index 00000000000..16a39cca955 --- /dev/null +++ b/docs/data/data-grid/filtering/CustomInputComponent.tsx.preview @@ -0,0 +1,14 @@ +=' }, + ], + }, + }, + }} +/> \ No newline at end of file diff --git a/docs/data/data-grid/filtering/CustomMultiValueOperator.js b/docs/data/data-grid/filtering/CustomMultiValueOperator.js new file mode 100644 index 00000000000..415a899b5dd --- /dev/null +++ b/docs/data/data-grid/filtering/CustomMultiValueOperator.js @@ -0,0 +1,180 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import TextField from '@mui/material/TextField'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import SyncIcon from '@mui/icons-material/Sync'; + +const SUBMIT_FILTER_STROKE_TIME = 500; + +function InputNumberInterval(props) { + const { item, applyValue, focusElementRef = null } = props; + + const filterTimeout = React.useRef(); + const [filterValueState, setFilterValueState] = React.useState(item.value ?? ''); + + const [applying, setIsApplying] = React.useState(false); + + React.useEffect(() => { + return () => { + clearTimeout(filterTimeout.current); + }; + }, []); + + React.useEffect(() => { + const itemValue = item.value ?? [undefined, undefined]; + setFilterValueState(itemValue); + }, [item.value]); + + const updateFilterValue = (lowerBound, upperBound) => { + clearTimeout(filterTimeout.current); + setFilterValueState([lowerBound, upperBound]); + + setIsApplying(true); + filterTimeout.current = setTimeout(() => { + setIsApplying(false); + applyValue({ ...item, value: [lowerBound, upperBound] }); + }, SUBMIT_FILTER_STROKE_TIME); + }; + + const handleUpperFilterChange = (event) => { + const newUpperBound = event.target.value; + updateFilterValue(filterValueState[0], newUpperBound); + }; + const handleLowerFilterChange = (event) => { + const newLowerBound = event.target.value; + updateFilterValue(newLowerBound, filterValueState[1]); + }; + + return ( + + + } : {}} + /> + + ); +} + +InputNumberInterval.propTypes = { + applyValue: PropTypes.func.isRequired, + focusElementRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ + current: PropTypes.any.isRequired, + }), + ]), + item: PropTypes.shape({ + /** + * The column from which we want to filter the rows. + */ + columnField: PropTypes.string.isRequired, + /** + * Must be unique. + * Only useful when the model contains several items. + */ + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + /** + * The name of the operator we want to apply. + * Will become required on `@mui/x-data-grid@6.X`. + */ + operatorValue: PropTypes.string, + /** + * The filtering value. + * The operator filtering function will decide for each row if the row values is correct compared to this value. + */ + value: PropTypes.any, + }).isRequired, +}; + +const quantityOnlyOperators = [ + { + label: 'Between', + value: 'between', + getApplyFilterFn: (filterItem) => { + if (!Array.isArray(filterItem.value) || filterItem.value.length !== 2) { + return null; + } + if (filterItem.value[0] == null || filterItem.value[1] == null) { + return null; + } + + return ({ value }) => { + return ( + value !== null && + filterItem.value[0] <= value && + value <= filterItem.value[1] + ); + }; + }, + InputComponent: InputNumberInterval, + }, +]; + +export default function CustomMultiValueOperator() { + const { data } = useDemoData({ dataSet: 'Commodity', rowLength: 100 }); + + const [filterModel, setFilterModel] = React.useState({ + items: [ + { + id: 1, + columnField: 'quantity', + value: [5000, 15000], + operatorValue: 'between', + }, + ], + }); + + const columns = React.useMemo(() => { + const newColumns = [...data.columns]; + + if (newColumns.length > 0) { + const index = newColumns.findIndex((col) => col.field === 'quantity'); + const quantityColumn = newColumns[index]; + + newColumns[index] = { + ...quantityColumn, + filterOperators: quantityOnlyOperators, + }; + } + + return newColumns; + }, [data.columns]); + + return ( +
+ setFilterModel(model)} + /> +
+ ); +} diff --git a/docs/data/data-grid/filtering/CustomMultiValueOperator.tsx b/docs/data/data-grid/filtering/CustomMultiValueOperator.tsx new file mode 100644 index 00000000000..a3042eb1c5e --- /dev/null +++ b/docs/data/data-grid/filtering/CustomMultiValueOperator.tsx @@ -0,0 +1,155 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import TextField, { TextFieldProps } from '@mui/material/TextField'; +import { + GridFilterInputValueProps, + DataGrid, + GridFilterItem, + GridFilterModel, + GridFilterOperator, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import SyncIcon from '@mui/icons-material/Sync'; + +const SUBMIT_FILTER_STROKE_TIME = 500; + +function InputNumberInterval(props: GridFilterInputValueProps) { + const { item, applyValue, focusElementRef = null } = props; + + const filterTimeout = React.useRef(); + const [filterValueState, setFilterValueState] = React.useState<[string, string]>( + item.value ?? '', + ); + const [applying, setIsApplying] = React.useState(false); + + React.useEffect(() => { + return () => { + clearTimeout(filterTimeout.current); + }; + }, []); + + React.useEffect(() => { + const itemValue = item.value ?? [undefined, undefined]; + setFilterValueState(itemValue); + }, [item.value]); + + const updateFilterValue = (lowerBound: string, upperBound: string) => { + clearTimeout(filterTimeout.current); + setFilterValueState([lowerBound, upperBound]); + + setIsApplying(true); + filterTimeout.current = setTimeout(() => { + setIsApplying(false); + applyValue({ ...item, value: [lowerBound, upperBound] }); + }, SUBMIT_FILTER_STROKE_TIME); + }; + + const handleUpperFilterChange: TextFieldProps['onChange'] = (event) => { + const newUpperBound = event.target.value; + updateFilterValue(filterValueState[0], newUpperBound); + }; + const handleLowerFilterChange: TextFieldProps['onChange'] = (event) => { + const newLowerBound = event.target.value; + updateFilterValue(newLowerBound, filterValueState[1]); + }; + + return ( + + + } : {}} + /> + + ); +} + +const quantityOnlyOperators: GridFilterOperator[] = [ + { + label: 'Between', + value: 'between', + getApplyFilterFn: (filterItem: GridFilterItem) => { + if (!Array.isArray(filterItem.value) || filterItem.value.length !== 2) { + return null; + } + if (filterItem.value[0] == null || filterItem.value[1] == null) { + return null; + } + + return ({ value }) => { + return ( + value !== null && + filterItem.value[0] <= value && + value <= filterItem.value[1] + ); + }; + }, + InputComponent: InputNumberInterval, + }, +]; + +export default function CustomMultiValueOperator() { + const { data } = useDemoData({ dataSet: 'Commodity', rowLength: 100 }); + + const [filterModel, setFilterModel] = React.useState({ + items: [ + { + id: 1, + columnField: 'quantity', + value: [5000, 15000], + operatorValue: 'between', + }, + ], + }); + + const columns = React.useMemo(() => { + const newColumns = [...data.columns]; + + if (newColumns.length > 0) { + const index = newColumns.findIndex((col) => col.field === 'quantity'); + const quantityColumn = newColumns[index]; + + newColumns[index] = { + ...quantityColumn, + filterOperators: quantityOnlyOperators, + }; + } + + return newColumns; + }, [data.columns]); + + return ( +
+ setFilterModel(model)} + /> +
+ ); +} diff --git a/docs/data/data-grid/filtering/CustomMultiValueOperator.tsx.preview b/docs/data/data-grid/filtering/CustomMultiValueOperator.tsx.preview new file mode 100644 index 00000000000..b630f94e26d --- /dev/null +++ b/docs/data/data-grid/filtering/CustomMultiValueOperator.tsx.preview @@ -0,0 +1,6 @@ + setFilterModel(model)} +/> \ No newline at end of file diff --git a/docs/data/data-grid/filtering/CustomRatingOperator.js b/docs/data/data-grid/filtering/CustomRatingOperator.js new file mode 100644 index 00000000000..c7a308e1173 --- /dev/null +++ b/docs/data/data-grid/filtering/CustomRatingOperator.js @@ -0,0 +1,144 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import Rating from '@mui/material/Rating'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function RatingInputValue(props) { + const { item, applyValue, focusElementRef } = props; + + const ratingRef = React.useRef(null); + React.useImperativeHandle(focusElementRef, () => ({ + focus: () => { + ratingRef.current + .querySelector(`input[value="${Number(item.value) || ''}"]`) + .focus(); + }, + })); + + const handleFilterChange = (event, newValue) => { + applyValue({ ...item, value: newValue }); + }; + + return ( + + + + ); +} + +RatingInputValue.propTypes = { + applyValue: PropTypes.func.isRequired, + focusElementRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ + current: PropTypes.any.isRequired, + }), + ]), + item: PropTypes.shape({ + /** + * The column from which we want to filter the rows. + */ + columnField: PropTypes.string.isRequired, + /** + * Must be unique. + * Only useful when the model contains several items. + */ + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + /** + * The name of the operator we want to apply. + * Will become required on `@mui/x-data-grid@6.X`. + */ + operatorValue: PropTypes.string, + /** + * The filtering value. + * The operator filtering function will decide for each row if the row values is correct compared to this value. + */ + value: PropTypes.any, + }).isRequired, +}; + +const ratingOnlyOperators = [ + { + label: 'Above', + value: 'above', + getApplyFilterFn: (filterItem) => { + if ( + !filterItem.columnField || + !filterItem.value || + !filterItem.operatorValue + ) { + return null; + } + + return (params) => { + return Number(params.value) >= Number(filterItem.value); + }; + }, + InputComponent: RatingInputValue, + InputComponentProps: { type: 'number' }, + }, +]; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function CustomRatingOperator() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + const columns = React.useMemo( + () => + data.columns.map((col) => + col.field === 'rating' + ? { + ...col, + filterOperators: ratingOnlyOperators, + } + : col, + ), + [data.columns], + ); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/CustomRatingOperator.tsx b/docs/data/data-grid/filtering/CustomRatingOperator.tsx new file mode 100644 index 00000000000..1ce8e2deebf --- /dev/null +++ b/docs/data/data-grid/filtering/CustomRatingOperator.tsx @@ -0,0 +1,117 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Rating, { RatingProps } from '@mui/material/Rating'; +import { + GridFilterInputValueProps, + DataGrid, + GridFilterItem, + GridFilterOperator, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function RatingInputValue(props: GridFilterInputValueProps) { + const { item, applyValue, focusElementRef } = props; + + const ratingRef: React.Ref = React.useRef(null); + React.useImperativeHandle(focusElementRef, () => ({ + focus: () => { + ratingRef.current + .querySelector(`input[value="${Number(item.value) || ''}"]`) + .focus(); + }, + })); + + const handleFilterChange: RatingProps['onChange'] = (event, newValue) => { + applyValue({ ...item, value: newValue }); + }; + + return ( + + + + ); +} + +const ratingOnlyOperators: GridFilterOperator[] = [ + { + label: 'Above', + value: 'above', + getApplyFilterFn: (filterItem: GridFilterItem) => { + if ( + !filterItem.columnField || + !filterItem.value || + !filterItem.operatorValue + ) { + return null; + } + + return (params): boolean => { + return Number(params.value) >= Number(filterItem.value); + }; + }, + InputComponent: RatingInputValue, + InputComponentProps: { type: 'number' }, + }, +]; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function CustomRatingOperator() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + const columns = React.useMemo( + () => + data.columns.map((col) => + col.field === 'rating' + ? { + ...col, + filterOperators: ratingOnlyOperators, + } + : col, + ), + [data.columns], + ); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/CustomSelectionOperator.js b/docs/data/data-grid/filtering/CustomSelectionOperator.js new file mode 100644 index 00000000000..7573fa9ae86 --- /dev/null +++ b/docs/data/data-grid/filtering/CustomSelectionOperator.js @@ -0,0 +1,110 @@ +import * as React from 'react'; +import { + DataGrid, + getGridDefaultColumnTypes, + DEFAULT_GRID_COL_TYPE_KEY, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +const defaultColumnTypes = getGridDefaultColumnTypes(); + +export default function CustomSelectionOperator() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + const [models, setModels] = React.useState(() => ({ + filterModel: { + items: [ + { + columnField: 'col1', + operatorValue: 'contains', + value: 'lo', + }, + ], + }, + selectionModel: [5], + })); + + const selectionModelLookup = React.useMemo( + () => + models.selectionModel.reduce((lookup, rowId) => { + lookup[rowId] = rowId; + return lookup; + }, {}), + [models.selectionModel], + ); + + const selectionModelLookupRef = React.useRef(selectionModelLookup); + selectionModelLookupRef.current = selectionModelLookup; + + const columns = React.useMemo(() => { + /** + * Function that takes an operator and wrap it to skip filtering for selected rows. + */ + const wrapOperator = (operator) => { + const getApplyFilterFn = (filterItem, column) => { + const innerFilterFn = operator.getApplyFilterFn(filterItem, column); + if (!innerFilterFn) { + return innerFilterFn; + } + + return (params) => { + if (selectionModelLookupRef.current[params.id]) { + return true; + } + + return innerFilterFn(params); + }; + }; + + return { + ...operator, + getApplyFilterFn, + }; + }; + + return data.columns.map((col) => { + const filterOperators = + col.filterOperators ?? + defaultColumnTypes[col.type ?? DEFAULT_GRID_COL_TYPE_KEY].filterOperators; + + return { + ...col, + filterOperators: filterOperators.map((operator) => wrapOperator(operator)), + }; + }); + }, [data.columns]); + + const handleSelectionModelChange = React.useCallback( + (newSelectionModel) => + setModels((prev) => ({ + ...prev, + selectionModel: newSelectionModel, + // Forces the re-application of the filtering process + filterModel: { ...prev.filterModel }, + })), + [], + ); + + const handleFilterModelChange = React.useCallback( + (newFilterModel) => + setModels((prev) => ({ ...prev, filterModel: newFilterModel })), + [], + ); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/CustomSelectionOperator.tsx b/docs/data/data-grid/filtering/CustomSelectionOperator.tsx new file mode 100644 index 00000000000..35937d97098 --- /dev/null +++ b/docs/data/data-grid/filtering/CustomSelectionOperator.tsx @@ -0,0 +1,124 @@ +import * as React from 'react'; +import { + DataGrid, + GridSelectionModel, + GridFilterModel, + GridFilterItem, + GridRowId, + GridFilterOperator, + GridStateColDef, + GridCellParams, + getGridDefaultColumnTypes, + DEFAULT_GRID_COL_TYPE_KEY, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +const defaultColumnTypes = getGridDefaultColumnTypes(); + +export default function CustomSelectionOperator() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + const [models, setModels] = React.useState<{ + selectionModel: GridSelectionModel; + filterModel: GridFilterModel; + }>(() => ({ + filterModel: { + items: [ + { + columnField: 'col1', + operatorValue: 'contains', + value: 'lo', + }, + ], + }, + selectionModel: [5], + })); + + const selectionModelLookup = React.useMemo( + () => + models.selectionModel.reduce>((lookup, rowId) => { + lookup[rowId] = rowId; + return lookup; + }, {}), + [models.selectionModel], + ); + + const selectionModelLookupRef = + React.useRef>(selectionModelLookup); + selectionModelLookupRef.current = selectionModelLookup; + + const columns = React.useMemo(() => { + /** + * Function that takes an operator and wrap it to skip filtering for selected rows. + */ + const wrapOperator = (operator: GridFilterOperator) => { + const getApplyFilterFn = ( + filterItem: GridFilterItem, + column: GridStateColDef, + ) => { + const innerFilterFn = operator.getApplyFilterFn(filterItem, column); + if (!innerFilterFn) { + return innerFilterFn; + } + + return (params: GridCellParams) => { + if (selectionModelLookupRef.current[params.id]) { + return true; + } + + return innerFilterFn(params); + }; + }; + + return { + ...operator, + getApplyFilterFn, + }; + }; + + return data.columns.map((col) => { + const filterOperators = + col.filterOperators ?? + defaultColumnTypes[col.type ?? DEFAULT_GRID_COL_TYPE_KEY].filterOperators!; + + return { + ...col, + filterOperators: filterOperators.map((operator) => wrapOperator(operator)), + }; + }); + }, [data.columns]); + + const handleSelectionModelChange = React.useCallback( + (newSelectionModel: GridSelectionModel) => + setModels((prev) => ({ + ...prev, + selectionModel: newSelectionModel, + // Forces the re-application of the filtering process + filterModel: { ...prev.filterModel }, + })), + [], + ); + + const handleFilterModelChange = React.useCallback( + (newFilterModel: GridFilterModel) => + setModels((prev) => ({ ...prev, filterModel: newFilterModel })), + [], + ); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/CustomSelectionOperator.tsx.preview b/docs/data/data-grid/filtering/CustomSelectionOperator.tsx.preview new file mode 100644 index 00000000000..8d9c6cdb43d --- /dev/null +++ b/docs/data/data-grid/filtering/CustomSelectionOperator.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/filtering/DisableFilteringGridAllColumns.js b/docs/data/data-grid/filtering/DisableFilteringGridAllColumns.js new file mode 100644 index 00000000000..e542265edf9 --- /dev/null +++ b/docs/data/data-grid/filtering/DisableFilteringGridAllColumns.js @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function DisableFilteringGridAllColumns() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/DisableFilteringGridAllColumns.tsx b/docs/data/data-grid/filtering/DisableFilteringGridAllColumns.tsx new file mode 100644 index 00000000000..e542265edf9 --- /dev/null +++ b/docs/data/data-grid/filtering/DisableFilteringGridAllColumns.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function DisableFilteringGridAllColumns() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/DisableFilteringGridAllColumns.tsx.preview b/docs/data/data-grid/filtering/DisableFilteringGridAllColumns.tsx.preview new file mode 100644 index 00000000000..b5d88f3e8fb --- /dev/null +++ b/docs/data/data-grid/filtering/DisableFilteringGridAllColumns.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/filtering/DisableFilteringGridSomeColumns.js b/docs/data/data-grid/filtering/DisableFilteringGridSomeColumns.js new file mode 100644 index 00000000000..a6913fd319a --- /dev/null +++ b/docs/data/data-grid/filtering/DisableFilteringGridSomeColumns.js @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function DisableFilteringGridSomeColumns() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + const columns = React.useMemo( + () => + data.columns.map((col) => + col.field === 'rating' ? { ...col, filterable: false } : col, + ), + [data.columns], + ); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/DisableFilteringGridSomeColumns.tsx b/docs/data/data-grid/filtering/DisableFilteringGridSomeColumns.tsx new file mode 100644 index 00000000000..a6913fd319a --- /dev/null +++ b/docs/data/data-grid/filtering/DisableFilteringGridSomeColumns.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function DisableFilteringGridSomeColumns() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + const columns = React.useMemo( + () => + data.columns.map((col) => + col.field === 'rating' ? { ...col, filterable: false } : col, + ), + [data.columns], + ); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/DisableFilteringGridSomeColumns.tsx.preview b/docs/data/data-grid/filtering/DisableFilteringGridSomeColumns.tsx.preview new file mode 100644 index 00000000000..50c4ffdb6a3 --- /dev/null +++ b/docs/data/data-grid/filtering/DisableFilteringGridSomeColumns.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/filtering/FilterApiNoSnap.js b/docs/data/data-grid/filtering/FilterApiNoSnap.js new file mode 100644 index 00000000000..d9ce729147a --- /dev/null +++ b/docs/data/data-grid/filtering/FilterApiNoSnap.js @@ -0,0 +1,7 @@ +import React from 'react'; +import ApiDocs from 'docsx/src/modules/components/ApiDocs'; +import api from 'docsx/pages/x/api/data-grid/grid-filter-api.json'; + +export default function FilterApiNoSnap() { + return ; +} diff --git a/docs/data/data-grid/filtering/FilterSelectorsNoSnap.js b/docs/data/data-grid/filtering/FilterSelectorsNoSnap.js new file mode 100644 index 00000000000..87f3b244b62 --- /dev/null +++ b/docs/data/data-grid/filtering/FilterSelectorsNoSnap.js @@ -0,0 +1,6 @@ +import React from 'react'; +import SelectorsDocs from 'docsx/src/modules/components/SelectorsDocs'; + +export default function FilterSelectorsNoSnap() { + return ; +} diff --git a/docs/data/data-grid/filtering/InitialFilters.js b/docs/data/data-grid/filtering/InitialFilters.js new file mode 100644 index 00000000000..891001e0b08 --- /dev/null +++ b/docs/data/data-grid/filtering/InitialFilters.js @@ -0,0 +1,38 @@ +import * as React from 'react'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function InitialFilters() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + return ( +
+ ', + value: '2.5', + }, + ], + }, + }, + }} + /> +
+ ); +} diff --git a/docs/data/data-grid/filtering/InitialFilters.tsx b/docs/data/data-grid/filtering/InitialFilters.tsx new file mode 100644 index 00000000000..891001e0b08 --- /dev/null +++ b/docs/data/data-grid/filtering/InitialFilters.tsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function InitialFilters() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + return ( +
+ ', + value: '2.5', + }, + ], + }, + }, + }} + /> +
+ ); +} diff --git a/docs/data/data-grid/filtering/QuickFilteringCustomLogic.js b/docs/data/data-grid/filtering/QuickFilteringCustomLogic.js new file mode 100644 index 00000000000..fde12c345dd --- /dev/null +++ b/docs/data/data-grid/filtering/QuickFilteringCustomLogic.js @@ -0,0 +1,70 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid, GridToolbarQuickFilter } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function QuickSearchToolbar() { + return ( + + + + ); +} + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +const getApplyFilterFnSameYear = (value) => { + if (!value || value.length !== 4 || !/\d{4}/.test(value)) { + // If the value is not a 4 digit string, it can not be a year so applying this filter is useless + return null; + } + return (params) => { + return params.value.getFullYear() === Number(value); + }; +}; + +export default function QuickFilteringCustomLogic() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + // Otherwise filter will be applied on fields such as the hidden column id + const columns = React.useMemo( + () => + data.columns + .filter((column) => VISIBLE_FIELDS.includes(column.field)) + .map((column) => { + if (column.field === 'dateCreated') { + return { + ...column, + getApplyQuickFilterFn: getApplyFilterFnSameYear, + }; + } + if (column.field === 'name') { + return { + ...column, + getApplyQuickFilterFn: undefined, + }; + } + return column; + }), + [data.columns], + ); + + return ( + + + + ); +} diff --git a/docs/data/data-grid/filtering/QuickFilteringCustomLogic.tsx b/docs/data/data-grid/filtering/QuickFilteringCustomLogic.tsx new file mode 100644 index 00000000000..845e1948a80 --- /dev/null +++ b/docs/data/data-grid/filtering/QuickFilteringCustomLogic.tsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid, GridCellParams, GridToolbarQuickFilter } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function QuickSearchToolbar() { + return ( + + + + ); +} + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +const getApplyFilterFnSameYear = (value: string) => { + if (!value || value.length !== 4 || !/\d{4}/.test(value)) { + // If the value is not a 4 digit string, it can not be a year so applying this filter is useless + return null; + } + return (params: GridCellParams): boolean => { + return params.value.getFullYear() === Number(value); + }; +}; + +export default function QuickFilteringCustomLogic() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + // Otherwise filter will be applied on fields such as the hidden column id + const columns = React.useMemo( + () => + data.columns + .filter((column) => VISIBLE_FIELDS.includes(column.field)) + .map((column) => { + if (column.field === 'dateCreated') { + return { + ...column, + getApplyQuickFilterFn: getApplyFilterFnSameYear, + }; + } + if (column.field === 'name') { + return { + ...column, + getApplyQuickFilterFn: undefined, + }; + } + return column; + }), + [data.columns], + ); + + return ( + + + + ); +} diff --git a/docs/data/data-grid/filtering/QuickFilteringCustomLogic.tsx.preview b/docs/data/data-grid/filtering/QuickFilteringCustomLogic.tsx.preview new file mode 100644 index 00000000000..fab619c968d --- /dev/null +++ b/docs/data/data-grid/filtering/QuickFilteringCustomLogic.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/filtering/QuickFilteringCustomizedGrid.js b/docs/data/data-grid/filtering/QuickFilteringCustomizedGrid.js new file mode 100644 index 00000000000..d972ff92b56 --- /dev/null +++ b/docs/data/data-grid/filtering/QuickFilteringCustomizedGrid.js @@ -0,0 +1,62 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { + DataGrid, + GridToolbarQuickFilter, + GridLinkOperator, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function QuickSearchToolbar() { + return ( + + + searchInput + .split(',') + .map((value) => value.trim()) + .filter((value) => value !== '') + } + /> + + ); +} + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function QuickFilteringCustomizedGrid() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + // Otherwise filter will be applied on fields such as the hidden column id + const columns = React.useMemo( + () => data.columns.filter((column) => VISIBLE_FIELDS.includes(column.field)), + [data.columns], + ); + + return ( + + + + ); +} diff --git a/docs/data/data-grid/filtering/QuickFilteringCustomizedGrid.tsx b/docs/data/data-grid/filtering/QuickFilteringCustomizedGrid.tsx new file mode 100644 index 00000000000..0ce9df96cf0 --- /dev/null +++ b/docs/data/data-grid/filtering/QuickFilteringCustomizedGrid.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { + DataGrid, + GridToolbarQuickFilter, + GridLinkOperator, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function QuickSearchToolbar() { + return ( + + + searchInput + .split(',') + .map((value) => value.trim()) + .filter((value) => value !== '') + } + /> + + ); +} + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function QuickFilteringCustomizedGrid() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + // Otherwise filter will be applied on fields such as the hidden column id + const columns = React.useMemo( + () => data.columns.filter((column) => VISIBLE_FIELDS.includes(column.field)), + [data.columns], + ); + + return ( + + + + ); +} diff --git a/docs/data/data-grid/filtering/QuickFilteringCustomizedGrid.tsx.preview b/docs/data/data-grid/filtering/QuickFilteringCustomizedGrid.tsx.preview new file mode 100644 index 00000000000..0580e47d5f3 --- /dev/null +++ b/docs/data/data-grid/filtering/QuickFilteringCustomizedGrid.tsx.preview @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/filtering/QuickFilteringGrid.js b/docs/data/data-grid/filtering/QuickFilteringGrid.js new file mode 100644 index 00000000000..c0e4e409382 --- /dev/null +++ b/docs/data/data-grid/filtering/QuickFilteringGrid.js @@ -0,0 +1,39 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function QuickFilteringGrid() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + // Otherwise filter will be applied on fields such as the hidden column id + const columns = React.useMemo( + () => data.columns.filter((column) => VISIBLE_FIELDS.includes(column.field)), + [data.columns], + ); + + return ( + + + + ); +} diff --git a/docs/data/data-grid/filtering/QuickFilteringGrid.tsx b/docs/data/data-grid/filtering/QuickFilteringGrid.tsx new file mode 100644 index 00000000000..c0e4e409382 --- /dev/null +++ b/docs/data/data-grid/filtering/QuickFilteringGrid.tsx @@ -0,0 +1,39 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function QuickFilteringGrid() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + // Otherwise filter will be applied on fields such as the hidden column id + const columns = React.useMemo( + () => data.columns.filter((column) => VISIBLE_FIELDS.includes(column.field)), + [data.columns], + ); + + return ( + + + + ); +} diff --git a/docs/data/data-grid/filtering/QuickFilteringGrid.tsx.preview b/docs/data/data-grid/filtering/QuickFilteringGrid.tsx.preview new file mode 100644 index 00000000000..f39e7f6346d --- /dev/null +++ b/docs/data/data-grid/filtering/QuickFilteringGrid.tsx.preview @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/filtering/RemoveBuiltInOperators.js b/docs/data/data-grid/filtering/RemoveBuiltInOperators.js new file mode 100644 index 00000000000..2873a8e6c4b --- /dev/null +++ b/docs/data/data-grid/filtering/RemoveBuiltInOperators.js @@ -0,0 +1,47 @@ +import * as React from 'react'; +import { DataGrid, getGridNumericOperators } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function RemoveBuiltInOperators() { + const { data } = useDemoData({ + dataSet: 'Employee', + rowLength: 100, + visibleFields: VISIBLE_FIELDS, + }); + + const columns = React.useMemo( + () => + data.columns.map((col) => { + if (col.field !== 'rating') { + return col; + } + + return { + ...col, + filterOperators: getGridNumericOperators().filter( + (operator) => operator.value === '>' || operator.value === '<', + ), + }; + }), + [data.columns], + ); + + return ( +
+ ' }], + }, + }, + }} + /> +
+ ); +} diff --git a/docs/data/data-grid/filtering/RemoveBuiltInOperators.tsx b/docs/data/data-grid/filtering/RemoveBuiltInOperators.tsx new file mode 100644 index 00000000000..2873a8e6c4b --- /dev/null +++ b/docs/data/data-grid/filtering/RemoveBuiltInOperators.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; +import { DataGrid, getGridNumericOperators } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function RemoveBuiltInOperators() { + const { data } = useDemoData({ + dataSet: 'Employee', + rowLength: 100, + visibleFields: VISIBLE_FIELDS, + }); + + const columns = React.useMemo( + () => + data.columns.map((col) => { + if (col.field !== 'rating') { + return col; + } + + return { + ...col, + filterOperators: getGridNumericOperators().filter( + (operator) => operator.value === '>' || operator.value === '<', + ), + }; + }), + [data.columns], + ); + + return ( +
+ ' }], + }, + }, + }} + /> +
+ ); +} diff --git a/docs/data/data-grid/filtering/RemoveBuiltInOperators.tsx.preview b/docs/data/data-grid/filtering/RemoveBuiltInOperators.tsx.preview new file mode 100644 index 00000000000..f4a0217bff6 --- /dev/null +++ b/docs/data/data-grid/filtering/RemoveBuiltInOperators.tsx.preview @@ -0,0 +1,12 @@ +' }], + }, + }, + }} +/> \ No newline at end of file diff --git a/docs/data/data-grid/filtering/ServerFilterGrid.js b/docs/data/data-grid/filtering/ServerFilterGrid.js new file mode 100644 index 00000000000..3eccab8fe3f --- /dev/null +++ b/docs/data/data-grid/filtering/ServerFilterGrid.js @@ -0,0 +1,28 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { createFakeServer } from '@mui/x-data-grid-generator'; + +const { columns, useQuery } = createFakeServer(); + +export default function ServerFilterGrid() { + const [queryOptions, setQueryOptions] = React.useState({}); + + const onFilterChange = React.useCallback((filterModel) => { + // Here you save the data you need from the filter model + setQueryOptions({ filterModel: { ...filterModel } }); + }, []); + + const { isLoading, data } = useQuery(queryOptions); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/ServerFilterGrid.tsx b/docs/data/data-grid/filtering/ServerFilterGrid.tsx new file mode 100644 index 00000000000..89de208646c --- /dev/null +++ b/docs/data/data-grid/filtering/ServerFilterGrid.tsx @@ -0,0 +1,28 @@ +import * as React from 'react'; +import { DataGrid, GridFilterModel } from '@mui/x-data-grid'; +import { createFakeServer } from '@mui/x-data-grid-generator'; + +const { columns, useQuery } = createFakeServer(); + +export default function ServerFilterGrid() { + const [queryOptions, setQueryOptions] = React.useState({}); + + const onFilterChange = React.useCallback((filterModel: GridFilterModel) => { + // Here you save the data you need from the filter model + setQueryOptions({ filterModel: { ...filterModel } }); + }, []); + + const { isLoading, data } = useQuery(queryOptions); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering/ServerFilterGrid.tsx.preview b/docs/data/data-grid/filtering/ServerFilterGrid.tsx.preview new file mode 100644 index 00000000000..dda5b76c08f --- /dev/null +++ b/docs/data/data-grid/filtering/ServerFilterGrid.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/filtering/filtering.md b/docs/data/data-grid/filtering/filtering.md new file mode 100644 index 00000000000..faf39f040bb --- /dev/null +++ b/docs/data/data-grid/filtering/filtering.md @@ -0,0 +1,421 @@ +--- +title: Data Grid - Filtering +--- + +# Data Grid - Filtering + +

Easily filter your rows based on one or several criteria.

+ +The filters can be modified through the grid interface in several ways: + +- By opening the column menu and clicking the _Filter_ menu item. +- By clicking the _Filters_ button in the grid toolbar (if enabled). + +Each column type has its own filter operators. +The demo below lets you explore all the operators for each built-in column type. + +_See [the dedicated section](#customize-the-operators) to learn how to create your own custom filter operator._ + +{{"demo": "BasicExampleDataGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +## Single and multi-filtering + +:::warning +The `DataGrid` can only filter the rows according to one criterion at the time. + +To use multi-filtering, you need to upgrade to the [Pro plan](https://mui.com/store/items/mui-x-pro/). +::: + +## Multi-filtering [](https://mui.com/store/items/mui-x-pro/) + +The following demo lets you filter the rows according to several criteria at the same time. + +{{"demo": "BasicExampleDataGridPro.js", "bg": "inline", "defaultCodeOpen": false}} + +## Pass filters to the grid + +### Structure of the model + +The full typing details can be found on the [GridFilterModel API page](/x/api/data-grid/grid-filter-model/). + +The filter model is composed of a list of `items` and a `linkOperator`: + +#### The `items` + +A filter item represents a filtering rule and is composed of several elements: + +- `filterItem.columnField`: the field on which we want to apply the rule. +- `filterItem.value`: the value to look for. +- `filterItem.operatorValue`: name of the operator method to use (e.g. _contains_), matches the `value` key of the operator object. +- `filterItem.id` ([](https://mui.com/store/items/mui-x-pro/)): only useful when multiple filters are used. + +:::info +Some operators do not need any value (for instance the `isEmpty` operator of the `string` column). +::: + +#### The `linkOperator` [](https://mui.com/store/items/mui-x-pro/) + +The `linkOperator` tells the grid if a row should satisfy all (`AND`) filter items or at least one (`OR`) in order to be considered valid. + +```ts +// Example 1: get rows with rating > 4 OR isAdmin = true +const filterModel: GridFilterModel = { + items: [ + { id: 1, columnField: 'rating', operatorValue: '>', value: '4' }, + { id: 2, columnField: 'isAdmin', operatorValue: 'is', value: 'true' }, + ], + linkOperator: GridLinkOperator.Or, +}; + +// Example 2: get rows with rating > 4 AND isAdmin = true +const filterModel: GridFilterModel = { + items: [ + { id: 1, columnField: 'rating', operatorValue: '>', value: '4' }, + { id: 2, columnField: 'isAdmin', operatorValue: 'is', value: 'true' }, + ], + linkOperator: GridLinkOperator.And, +}; +``` + +If no `linkOperator` is provided, the grid will use `GridLinkOperator.Or` by default. + +### Initialize the filters + +To initialize the filters without controlling them, provide the model to the `initialState` prop. + +```jsx +', value: '2.5' }], + }, + }, + }} +/> +``` + +{{"demo": "InitialFilters.js", "bg": "inline", "defaultCodeOpen": false}} + +### Controlled filters + +Use the `filterModel` prop to control the filter applied on the rows. + +You can use the `onFilterModelChange` prop to listen to changes to the filters and update the prop accordingly. + +```jsx +', value: '2.5' }], + }} +/> +``` + +{{"demo": "ControlledFilters.js", "bg": "inline", "defaultCodeOpen": false}} + +## Disable the filters + +### For all columns + +Filters are enabled by default, but you can easily disable this feature by setting the `disableColumnFilter` prop. + +```jsx + +``` + +{{"demo": "DisableFilteringGridAllColumns.js", "bg": "inline", "defaultCodeOpen": false}} + +### For some columns + +To disable the filter of a single column, set the `filterable` property in `GridColDef` to `false`. + +In the example below, the _rating_ column can not be filtered. + +```js + +``` + +{{"demo": "DisableFilteringGridSomeColumns.js", "bg": "inline", "defaultCodeOpen": false}} + +## Customize the operators + +The full typing details can be found on the [GridFilterOperator API page](/x/api/data-grid/grid-filter-operator/). + +An operator determines if a cell value should be considered as a valid filtered value. +The candidate value used by the operator is the one corresponding to the `field` attribute or the value returned by the `valueGetter` of the `GridColDef`. + +Each column type comes with a default array of operators. +You can get them by importing the following functions: + +| Column type | Function | +| -------------- | -------------------------------- | +| `string` | `getGridStringOperators()` | +| `number` | `getGridNumericOperators()` | +| `boolean` | `getGridBooleanOperators()` | +| `date` | `getGridDateOperators()` | +| `dateTime` | `getGridDateOperators(true)` | +| `singleSelect` | `getGridSingleSelectOperators()` | + +You can find more information about the supported column types in the [columns section](/x/react-data-grid/column-definition/#column-types). + +### Create a custom operator + +If the built-in filter operators are not enough, creating a custom operator is an option. +A custom operator is defined by creating a `GridFilterOperator` object. +This object has to be added to the `filterOperators` attribute of the `GridColDef`. + +The main part of an operator is the `getApplyFilterFn` function. +When applying the filters, the grid will call this function with the filter item and the column on which the item must be applied. +This function must return another function that takes the cell value as an input and return `true` if it satisfies the operator condition. + +```ts +const operator: GridFilterOperator = { + label: 'From', + value: 'from', + getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => { + if (!filterItem.columnField || !filterItem.value || !filterItem.operatorValue) { + return null; + } + + return (params: GridCellParams): boolean => { + return Number(params.value) >= Number(filterItem.value); + }; + }, + InputComponent: RatingInputValue, + InputComponentProps: { type: 'number' }, +}; +``` + +:::info +The [`valueFormatter`](/x/react-data-grid/column-definition/#value-formatter) is only used for rendering purposes. +::: + +:::info +If the column has a [`valueGetter`](/x/react-data-grid/column-definition/#value-getter), then `params.value` will be the resolved value. +::: + +In the demo below, you can see how to create a completely new operator for the Rating column. + +{{"demo": "CustomRatingOperator.js", "bg": "inline", "defaultCodeOpen": false}} + +### Wrap built-in operators + +You can create custom operators that re-use the logic of the built-in ones. + +In the demo below, the selected rows are always visible even when they don't match the filtering rules. + +{{"demo": "CustomSelectionOperator.js", "bg": "inline", "defaultCodeOpen": false}} + +### Multiple values operator + +You can create a custom operator which accepts multiple values. To do this, provide an array of values to the `value` property of the `filterItem`. +The `valueParser` of the `GridColDef` will be applied to each item of the array. + +The filtering function `getApplyFilterFn` must be adapted to handle `filterItem.value` as an array. +Below is an example for a "between" operator, applied on the "Quantity" column. + +```ts +{ + label: 'Between', + value: 'between', + getApplyFilterFn: (filterItem: GridFilterItem) => { + if (!Array.isArray(filterItem.value) || filterItem.value.length !== 2) { + return null; + } + if (filterItem.value[0] == null || filterItem.value[1] == null) { + return null; + } + return ({ value }): boolean => { + return value != null && filterItem.value[0] <= value && value <= filterItem.value[1]; + }; + }, + InputComponent: InputNumberInterval, +} +``` + +{{"demo": "CustomMultiValueOperator.js", "bg": "inline", "defaultCodeOpen": false}} + +### Remove an operator + +To remove built-in operators, import the method to generate them and filter the output to fit your needs. + +```ts +// Only keep '>' and '<' default operators +const filterOperators = getGridNumericOperators().filter( + (operator) => operator.value === '>' || operator.value === '<', +); +``` + +In the demo below, the `rating` column only has the `<` and `>` operators. + +{{"demo": "RemoveBuiltInOperators.js", "bg": "inline", "defaultCodeOpen": false}} + +### Custom input component + +The value used by the operator to look for has to be entered by the user. +On most column types, a text field is used. +However, a custom component can be rendered instead. + +In the demo below, the `rating` column reuses the numeric operators but the rating component is used to enter the value of the filter. + +{{"demo": "CustomInputComponent.js", "bg": "inline", "defaultCodeOpen": false}} + +### Custom column types + +When defining a [custom column type](/x/react-data-grid/column-definition/#custom-column-types), by default the grid will reuse the operators from the type that was extended. +The filter operators can then be edited just like on a regular column. + +```ts +const ratingColumnType: GridColTypeDef = { + extendType: 'number', + filterOperators: getGridNumericOperators().filter( + (operator) => operator.value === '>' || operator.value === '<', + ), +}; +``` + +## Custom filter panel + +You can customize the rendering of the filter panel as shown in [the component section](/x/react-data-grid/components/#overriding-components) of the documentation. + +### Customize the filter panel content + +The customization of the filter panel content can be performed by passing props to the default [``](/x/api/data-grid/grid-filter-panel/) component. +The available props allow overriding: + +- The `linkOperators` (can contains `GridLinkOperator.And` and `GridLinkOperator.Or`) +- The order of the column selector (can be `"asc"` or `"desc"`) +- Any prop of the input components + +Input components can be [customized](/material-ui/customization/how-to-customize/) by using two approaches. +You can pass a `sx` prop to any input container or you can use CSS selectors on nested components of the filter panel. +More details are available in the demo. + +| Props | CSS class | +| :----------------------- | :---------------------------------------- | +| `deleteIconProps` | `MuiDataGrid-filterFormDeleteIcon` | +| `linkOperatorInputProps` | `MuiDataGrid-filterFormLinkOperatorInput` | +| `columnInputProps` | `MuiDataGrid-filterFormColumnInput` | +| `operatorInputProps` | `MuiDataGrid-filterFormOperatorInput` | +| `valueInputProps` | `MuiDataGrid-filterFormValueInput` | + +{{"demo": "CustomFilterPanelContent.js", "bg": "inline"}} + +### Customize the filter panel position + +The demo below shows how to anchor the filter panel to the toolbar button instead of the column header. + +{{"demo": "CustomFilterPanelPosition.js", "bg": "inline", "defaultCodeOpen": false}} + +## Server-side filter + +Filtering can be run server-side by setting the `filterMode` prop to `server`, and implementing the `onFilterModelChange` handler. + +The example below demonstrates how to achieve server-side filtering. + +{{"demo": "ServerFilterGrid.js", "bg": "inline"}} + +## Quick filter + +Quick filter allows filtering rows by multiple columns with a single text input. +To enable it, you can add the `` component to your custom toolbar or pass `showQuickFilter` to the default ``. + +By default, the quick filter considers the input as a list of values separated by space and keeps only rows that contain all the values. + +{{"demo": "QuickFilteringGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +### Custom filtering logic + +The logic used for quick filter can be switched to filter rows that contain _at least_ one of the values specified instead of testing if it contains all of them. +To do so, set `quickFilterLogicOperator` to `GridLinkOperator.Or` as follow: + +```js +initialState={{ + filter: { + filterModel: { + items: [], + quickFilterLogicOperator: GridLinkOperator.Or, + }, + }, +}} +``` + +With the default settings, quick filter will only consider columns with types `'string'`,`'number'`, and `'singleSelect'`. + +- For `'string'` columns, the cell must **contain** the value +- For `'number'` columns, the cell must **equal** the value +- For `'singleSelect'` columns, the cell must **start with** the value + +To modify or add the quick filter operators, add the property `getApplyQuickFilterFn` to the column definition. +This function is quite similar to `getApplyFilterFn`. +This function takes as an input a value of the quick filter and returns another function that takes the cell value as an input and returns `true` if it satisfies the operator condition. + +In the example below, a custom filter is created for the `date` column to check if it contains the correct year. + +```ts +getApplyFilterFn: (value: string) => { + if (!value || value.length !== 4 || !/\d{4}/.test(value)) { + // If the value is not a 4 digit string, it can not be a year so applying this filter is useless + return null; + } + return (params: GridCellParams): boolean => { + return params.value.getFullYear() === Number(value); + }; +}; +``` + +To remove the quick filtering on a given column set `getApplyFilterFn: undefined`. + +In the demo bellow, the column "Name" is not searchable with the quick filter, and 4 digits figures will be compared to the year of column "Created on". + +{{"demo": "QuickFilteringCustomLogic.js", "bg": "inline", "defaultCodeOpen": false}} + +### Parsing values + +The values used by the quick filter are obtained by splitting with space. +If you want to implement a more advanced logic, the `` component accepts a prop `quickFilterParser`. +This function takes the string from the search text field and returns an array of values. + +If you control the `quickFilterValues` either by controlling `filterModel` or with the initial state, the content of the input must be updated to reflect the new values. +By default, values are joint with a spaces. You can customize this behavior by providing `quickFilterFormatter`. +This formatter can be seen as the inverse of the `quickFilterParser`. + +For example, the following parser allows to search words containing a space by using the `','` to split values. + +```jsx + + searchInput.split(',').map((value) => value.trim()) + } + quickFilterFormatter={(quickFilterValues) => quickFilterValues.join(', ')} + debounceMs={200} // time before applying the new quick filter value +/> +``` + +In the following demo, the quick filter value `"Saint Martin, Saint Lucia"` will return rows with country is Saint Martin or Saint Lucia. + +{{"demo": "QuickFilteringCustomizedGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +## apiRef [](https://mui.com/store/items/mui-x-pro/) + +:::warning +Only use this API as the last option. Give preference to the props to control the grid. +::: + +{{"demo": "FilterApiNoSnap.js", "bg": "inline", "hideToolbar": true}} + +## Selectors [](https://mui.com/store/items/mui-x-pro/) + +{{"demo": "FilterSelectorsNoSnap.js", "bg": "inline", "hideToolbar": true}} + +More information about the selectors and how to use them on the [dedicated page](/x/react-data-grid/state/#access-the-state) + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) +- [GridFilterForm](/x/api/data-grid/grid-filter-form/) +- [GridFilterItem](/x/api/data-grid/grid-filter-item/) +- [GridFilterModel](/x/api/data-grid/grid-filter-model/) +- [GridFilterOperator](/x/api/data-grid/grid-filter-operator/) +- [GridFilterPanel](/x/api/data-grid/grid-filter-panel/) diff --git a/docs/data/data-grid/getting-started/Codesandbox.js b/docs/data/data-grid/getting-started/Codesandbox.js new file mode 100644 index 00000000000..074b30ee7d5 --- /dev/null +++ b/docs/data/data-grid/getting-started/Codesandbox.js @@ -0,0 +1,16 @@ +import * as React from 'react'; + +export default function Codesandbox() { + return ( +