@multiplatform.one/frappe-ui 6.7.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/FrappeAssignment.cjs +2 -0
- package/dist/cjs/FrappeAssignment.native.js +2 -0
- package/dist/cjs/FrappeAssignment.native.js.map +1 -1
- package/dist/cjs/FrappeCalendar.cjs +5 -7
- package/dist/cjs/FrappeCalendar.native.js +5 -9
- package/dist/cjs/FrappeCalendar.native.js.map +1 -1
- package/dist/cjs/FrappeChartCard.cjs +2 -0
- package/dist/cjs/FrappeChartCard.native.js +2 -0
- package/dist/cjs/FrappeChartCard.native.js.map +1 -1
- package/dist/cjs/FrappeDashboard.cjs +11 -22
- package/dist/cjs/FrappeDashboard.native.js +20 -22
- package/dist/cjs/FrappeDashboard.native.js.map +1 -1
- package/dist/cjs/FrappeForm.cjs +3 -1
- package/dist/cjs/FrappeForm.native.js +3 -1
- package/dist/cjs/FrappeForm.native.js.map +1 -1
- package/dist/cjs/FrappeGantt.cjs +5 -7
- package/dist/cjs/FrappeGantt.native.js +5 -9
- package/dist/cjs/FrappeGantt.native.js.map +1 -1
- package/dist/cjs/FrappeImageView.cjs +3 -1
- package/dist/cjs/FrappeImageView.native.js +3 -1
- package/dist/cjs/FrappeImageView.native.js.map +1 -1
- package/dist/cjs/FrappeKPICard.cjs +2 -0
- package/dist/cjs/FrappeKPICard.native.js +2 -0
- package/dist/cjs/FrappeKPICard.native.js.map +1 -1
- package/dist/cjs/FrappeKanban.cjs +3 -1
- package/dist/cjs/FrappeKanban.native.js +3 -1
- package/dist/cjs/FrappeKanban.native.js.map +1 -1
- package/dist/cjs/FrappeLikedBy.cjs +2 -0
- package/dist/cjs/FrappeLikedBy.native.js +2 -0
- package/dist/cjs/FrappeLikedBy.native.js.map +1 -1
- package/dist/cjs/FrappeList.cjs +3 -1
- package/dist/cjs/FrappeList.native.js +3 -1
- package/dist/cjs/FrappeList.native.js.map +1 -1
- package/dist/cjs/FrappeListView.cjs +6 -12
- package/dist/cjs/FrappeListView.native.js +13 -12
- package/dist/cjs/FrappeListView.native.js.map +1 -1
- package/dist/cjs/FrappeProvider.cjs +5 -1
- package/dist/cjs/FrappeProvider.native.js +5 -1
- package/dist/cjs/FrappeProvider.native.js.map +1 -1
- package/dist/cjs/FrappeReportBuilder.cjs +2 -0
- package/dist/cjs/FrappeReportBuilder.native.js +2 -0
- package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
- package/dist/cjs/FrappeReportView.cjs +23 -45
- package/dist/cjs/FrappeReportView.native.js +35 -50
- package/dist/cjs/FrappeReportView.native.js.map +1 -1
- package/dist/cjs/FrappeTable.cjs +105 -31
- package/dist/cjs/FrappeTable.native.js +140 -42
- package/dist/cjs/FrappeTable.native.js.map +1 -1
- package/dist/cjs/FrappeTreeView.cjs +3 -1
- package/dist/cjs/FrappeTreeView.native.js +3 -1
- package/dist/cjs/FrappeTreeView.native.js.map +1 -1
- package/dist/cjs/SyncErrorNotice.cjs +76 -0
- package/dist/cjs/SyncErrorNotice.native.js +92 -0
- package/dist/cjs/SyncErrorNotice.native.js.map +1 -0
- package/dist/cjs/commentActions.cjs +120 -0
- package/dist/cjs/commentActions.native.js +129 -0
- package/dist/cjs/commentActions.native.js.map +1 -0
- package/dist/cjs/components/attachments.cjs +2 -0
- package/dist/cjs/components/attachments.native.js +2 -0
- package/dist/cjs/components/attachments.native.js.map +1 -1
- package/dist/cjs/components/tags.cjs +5 -0
- package/dist/cjs/components/tags.native.js +5 -0
- package/dist/cjs/components/tags.native.js.map +1 -1
- package/dist/cjs/components/timeline.cjs +29 -2
- package/dist/cjs/components/timeline.native.js +33 -2
- package/dist/cjs/components/timeline.native.js.map +1 -1
- package/dist/cjs/desk/DeskFormSidebar.cjs +2 -0
- package/dist/cjs/desk/DeskFormSidebar.native.js +2 -0
- package/dist/cjs/desk/DeskFormSidebar.native.js.map +1 -1
- package/dist/cjs/fieldRegistry.cjs +2 -0
- package/dist/cjs/fieldRegistry.native.js +2 -0
- package/dist/cjs/fieldRegistry.native.js.map +1 -1
- package/dist/cjs/fields/Date/index.cjs +71 -7
- package/dist/cjs/fields/Date/index.native.js +325 -37
- package/dist/cjs/fields/Date/index.native.js.map +1 -1
- package/dist/cjs/fields/Date/types.cjs +18 -0
- package/dist/cjs/fields/Date/types.native.js +21 -0
- package/dist/cjs/fields/Date/types.native.js.map +1 -0
- package/dist/cjs/fields/Date/wire.cjs +4 -8
- package/dist/cjs/fields/Date/wire.native.js +4 -11
- package/dist/cjs/fields/Date/wire.native.js.map +1 -1
- package/dist/cjs/fields/DynamicLink.cjs +2 -0
- package/dist/cjs/fields/DynamicLink.native.js +2 -0
- package/dist/cjs/fields/DynamicLink.native.js.map +1 -1
- package/dist/cjs/fields/FileUpload.cjs +2 -0
- package/dist/cjs/fields/FileUpload.native.js +2 -0
- package/dist/cjs/fields/FileUpload.native.js.map +1 -1
- package/dist/cjs/fields/TableMultiSelect.cjs +6 -1
- package/dist/cjs/fields/TableMultiSelect.native.js +6 -1
- package/dist/cjs/fields/TableMultiSelect.native.js.map +1 -1
- package/dist/cjs/frappeStoryTarget.cjs +88 -0
- package/dist/cjs/frappeStoryTarget.native.js +100 -0
- package/dist/cjs/frappeStoryTarget.native.js.map +1 -1
- package/dist/cjs/frappeUsers.cjs +2 -0
- package/dist/cjs/frappeUsers.native.js +2 -0
- package/dist/cjs/frappeUsers.native.js.map +1 -1
- package/dist/cjs/index.cjs +17 -4
- package/dist/cjs/index.native.js +17 -4
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/liveErrors.cjs +5 -1
- package/dist/cjs/liveErrors.native.js +5 -1
- package/dist/cjs/liveErrors.native.js.map +1 -1
- package/dist/cjs/useFrappeAggregate.cjs +13 -1
- package/dist/cjs/useFrappeAggregate.native.js +14 -1
- package/dist/cjs/useFrappeAggregate.native.js.map +1 -1
- package/dist/cjs/useFrappeFacets.cjs +329 -0
- package/dist/cjs/useFrappeFacets.native.js +632 -0
- package/dist/cjs/useFrappeFacets.native.js.map +1 -0
- package/dist/esm/FrappeAssignment.mjs +2 -0
- package/dist/esm/FrappeAssignment.mjs.map +1 -1
- package/dist/esm/FrappeAssignment.native.js +2 -0
- package/dist/esm/FrappeAssignment.native.js.map +1 -1
- package/dist/esm/FrappeCalendar.mjs +6 -8
- package/dist/esm/FrappeCalendar.mjs.map +1 -1
- package/dist/esm/FrappeCalendar.native.js +6 -10
- package/dist/esm/FrappeCalendar.native.js.map +1 -1
- package/dist/esm/FrappeChartCard.mjs +2 -0
- package/dist/esm/FrappeChartCard.mjs.map +1 -1
- package/dist/esm/FrappeChartCard.native.js +2 -0
- package/dist/esm/FrappeChartCard.native.js.map +1 -1
- package/dist/esm/FrappeDashboard.mjs +12 -19
- package/dist/esm/FrappeDashboard.mjs.map +1 -1
- package/dist/esm/FrappeDashboard.native.js +23 -21
- package/dist/esm/FrappeDashboard.native.js.map +1 -1
- package/dist/esm/FrappeForm.mjs +3 -1
- package/dist/esm/FrappeForm.mjs.map +1 -1
- package/dist/esm/FrappeForm.native.js +3 -1
- package/dist/esm/FrappeForm.native.js.map +1 -1
- package/dist/esm/FrappeGantt.mjs +6 -8
- package/dist/esm/FrappeGantt.mjs.map +1 -1
- package/dist/esm/FrappeGantt.native.js +6 -10
- package/dist/esm/FrappeGantt.native.js.map +1 -1
- package/dist/esm/FrappeImageView.mjs +3 -1
- package/dist/esm/FrappeImageView.mjs.map +1 -1
- package/dist/esm/FrappeImageView.native.js +3 -1
- package/dist/esm/FrappeImageView.native.js.map +1 -1
- package/dist/esm/FrappeKPICard.mjs +2 -0
- package/dist/esm/FrappeKPICard.mjs.map +1 -1
- package/dist/esm/FrappeKPICard.native.js +2 -0
- package/dist/esm/FrappeKPICard.native.js.map +1 -1
- package/dist/esm/FrappeKanban.mjs +3 -1
- package/dist/esm/FrappeKanban.mjs.map +1 -1
- package/dist/esm/FrappeKanban.native.js +3 -1
- package/dist/esm/FrappeKanban.native.js.map +1 -1
- package/dist/esm/FrappeLikedBy.mjs +2 -0
- package/dist/esm/FrappeLikedBy.mjs.map +1 -1
- package/dist/esm/FrappeLikedBy.native.js +2 -0
- package/dist/esm/FrappeLikedBy.native.js.map +1 -1
- package/dist/esm/FrappeList.mjs +3 -1
- package/dist/esm/FrappeList.mjs.map +1 -1
- package/dist/esm/FrappeList.native.js +3 -1
- package/dist/esm/FrappeList.native.js.map +1 -1
- package/dist/esm/FrappeListView.mjs +7 -13
- package/dist/esm/FrappeListView.mjs.map +1 -1
- package/dist/esm/FrappeListView.native.js +15 -14
- package/dist/esm/FrappeListView.native.js.map +1 -1
- package/dist/esm/FrappeProvider.mjs +5 -1
- package/dist/esm/FrappeProvider.mjs.map +1 -1
- package/dist/esm/FrappeProvider.native.js +5 -1
- package/dist/esm/FrappeProvider.native.js.map +1 -1
- package/dist/esm/FrappeReportBuilder.mjs +2 -0
- package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
- package/dist/esm/FrappeReportBuilder.native.js +2 -0
- package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
- package/dist/esm/FrappeReportView.mjs +26 -49
- package/dist/esm/FrappeReportView.mjs.map +1 -1
- package/dist/esm/FrappeReportView.native.js +38 -54
- package/dist/esm/FrappeReportView.native.js.map +1 -1
- package/dist/esm/FrappeTable.mjs +107 -33
- package/dist/esm/FrappeTable.mjs.map +1 -1
- package/dist/esm/FrappeTable.native.js +142 -44
- package/dist/esm/FrappeTable.native.js.map +1 -1
- package/dist/esm/FrappeTreeView.mjs +3 -1
- package/dist/esm/FrappeTreeView.mjs.map +1 -1
- package/dist/esm/FrappeTreeView.native.js +3 -1
- package/dist/esm/FrappeTreeView.native.js.map +1 -1
- package/dist/esm/SyncErrorNotice.mjs +51 -0
- package/dist/esm/SyncErrorNotice.mjs.map +1 -0
- package/dist/esm/SyncErrorNotice.native.js +53 -0
- package/dist/esm/SyncErrorNotice.native.js.map +1 -0
- package/dist/esm/commentActions.mjs +95 -0
- package/dist/esm/commentActions.mjs.map +1 -0
- package/dist/esm/commentActions.native.js +101 -0
- package/dist/esm/commentActions.native.js.map +1 -0
- package/dist/esm/components/attachments.mjs +2 -0
- package/dist/esm/components/attachments.mjs.map +1 -1
- package/dist/esm/components/attachments.native.js +2 -0
- package/dist/esm/components/attachments.native.js.map +1 -1
- package/dist/esm/components/tags.mjs +5 -0
- package/dist/esm/components/tags.mjs.map +1 -1
- package/dist/esm/components/tags.native.js +5 -0
- package/dist/esm/components/tags.native.js.map +1 -1
- package/dist/esm/components/timeline.mjs +31 -4
- package/dist/esm/components/timeline.mjs.map +1 -1
- package/dist/esm/components/timeline.native.js +35 -4
- package/dist/esm/components/timeline.native.js.map +1 -1
- package/dist/esm/desk/DeskFormSidebar.mjs +2 -0
- package/dist/esm/desk/DeskFormSidebar.mjs.map +1 -1
- package/dist/esm/desk/DeskFormSidebar.native.js +2 -0
- package/dist/esm/desk/DeskFormSidebar.native.js.map +1 -1
- package/dist/esm/fieldRegistry.mjs +2 -0
- package/dist/esm/fieldRegistry.mjs.map +1 -1
- package/dist/esm/fieldRegistry.native.js +2 -0
- package/dist/esm/fieldRegistry.native.js.map +1 -1
- package/dist/esm/fields/Date/index.mjs +73 -9
- package/dist/esm/fields/Date/index.mjs.map +1 -1
- package/dist/esm/fields/Date/index.native.js +315 -38
- package/dist/esm/fields/Date/index.native.js.map +1 -1
- package/dist/esm/fields/Date/types.mjs +2 -0
- package/dist/esm/fields/Date/types.mjs.map +1 -0
- package/dist/esm/fields/Date/types.native.js +2 -0
- package/dist/esm/fields/Date/types.native.js.map +1 -0
- package/dist/esm/fields/Date/wire.mjs +4 -8
- package/dist/esm/fields/Date/wire.mjs.map +1 -1
- package/dist/esm/fields/Date/wire.native.js +4 -11
- package/dist/esm/fields/Date/wire.native.js.map +1 -1
- package/dist/esm/fields/DynamicLink.mjs +2 -0
- package/dist/esm/fields/DynamicLink.mjs.map +1 -1
- package/dist/esm/fields/DynamicLink.native.js +2 -0
- package/dist/esm/fields/DynamicLink.native.js.map +1 -1
- package/dist/esm/fields/FileUpload.mjs +2 -0
- package/dist/esm/fields/FileUpload.mjs.map +1 -1
- package/dist/esm/fields/FileUpload.native.js +2 -0
- package/dist/esm/fields/FileUpload.native.js.map +1 -1
- package/dist/esm/fields/TableMultiSelect.mjs +7 -2
- package/dist/esm/fields/TableMultiSelect.mjs.map +1 -1
- package/dist/esm/fields/TableMultiSelect.native.js +7 -2
- package/dist/esm/fields/TableMultiSelect.native.js.map +1 -1
- package/dist/esm/frappeStoryTarget.mjs +88 -2
- package/dist/esm/frappeStoryTarget.mjs.map +1 -1
- package/dist/esm/frappeStoryTarget.native.js +100 -2
- package/dist/esm/frappeStoryTarget.native.js.map +1 -1
- package/dist/esm/frappeUsers.mjs +2 -0
- package/dist/esm/frappeUsers.mjs.map +1 -1
- package/dist/esm/frappeUsers.native.js +2 -0
- package/dist/esm/frappeUsers.native.js.map +1 -1
- package/dist/esm/index.mjs +7 -3
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +7 -3
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/liveErrors.mjs +5 -1
- package/dist/esm/liveErrors.mjs.map +1 -1
- package/dist/esm/liveErrors.native.js +5 -1
- package/dist/esm/liveErrors.native.js.map +1 -1
- package/dist/esm/useFrappeAggregate.mjs +13 -1
- package/dist/esm/useFrappeAggregate.mjs.map +1 -1
- package/dist/esm/useFrappeAggregate.native.js +14 -1
- package/dist/esm/useFrappeAggregate.native.js.map +1 -1
- package/dist/esm/useFrappeFacets.mjs +299 -0
- package/dist/esm/useFrappeFacets.mjs.map +1 -0
- package/dist/esm/useFrappeFacets.native.js +599 -0
- package/dist/esm/useFrappeFacets.native.js.map +1 -0
- package/dist/jsx/FrappeAssignment.mjs +2 -0
- package/dist/jsx/FrappeAssignment.mjs.map +1 -1
- package/dist/jsx/FrappeAssignment.native.js +2 -0
- package/dist/jsx/FrappeAssignment.native.js.map +1 -1
- package/dist/jsx/FrappeCalendar.mjs +6 -8
- package/dist/jsx/FrappeCalendar.mjs.map +1 -1
- package/dist/jsx/FrappeCalendar.native.js +5 -9
- package/dist/jsx/FrappeCalendar.native.js.map +1 -1
- package/dist/jsx/FrappeChartCard.mjs +2 -0
- package/dist/jsx/FrappeChartCard.mjs.map +1 -1
- package/dist/jsx/FrappeChartCard.native.js +2 -0
- package/dist/jsx/FrappeChartCard.native.js.map +1 -1
- package/dist/jsx/FrappeDashboard.mjs +12 -19
- package/dist/jsx/FrappeDashboard.mjs.map +1 -1
- package/dist/jsx/FrappeDashboard.native.js +20 -22
- package/dist/jsx/FrappeDashboard.native.js.map +1 -1
- package/dist/jsx/FrappeForm.mjs +3 -1
- package/dist/jsx/FrappeForm.mjs.map +1 -1
- package/dist/jsx/FrappeForm.native.js +3 -1
- package/dist/jsx/FrappeForm.native.js.map +1 -1
- package/dist/jsx/FrappeGantt.mjs +6 -8
- package/dist/jsx/FrappeGantt.mjs.map +1 -1
- package/dist/jsx/FrappeGantt.native.js +5 -9
- package/dist/jsx/FrappeGantt.native.js.map +1 -1
- package/dist/jsx/FrappeImageView.mjs +3 -1
- package/dist/jsx/FrappeImageView.mjs.map +1 -1
- package/dist/jsx/FrappeImageView.native.js +3 -1
- package/dist/jsx/FrappeImageView.native.js.map +1 -1
- package/dist/jsx/FrappeKPICard.mjs +2 -0
- package/dist/jsx/FrappeKPICard.mjs.map +1 -1
- package/dist/jsx/FrappeKPICard.native.js +2 -0
- package/dist/jsx/FrappeKPICard.native.js.map +1 -1
- package/dist/jsx/FrappeKanban.mjs +3 -1
- package/dist/jsx/FrappeKanban.mjs.map +1 -1
- package/dist/jsx/FrappeKanban.native.js +3 -1
- package/dist/jsx/FrappeKanban.native.js.map +1 -1
- package/dist/jsx/FrappeLikedBy.mjs +2 -0
- package/dist/jsx/FrappeLikedBy.mjs.map +1 -1
- package/dist/jsx/FrappeLikedBy.native.js +2 -0
- package/dist/jsx/FrappeLikedBy.native.js.map +1 -1
- package/dist/jsx/FrappeList.mjs +3 -1
- package/dist/jsx/FrappeList.mjs.map +1 -1
- package/dist/jsx/FrappeList.native.js +3 -1
- package/dist/jsx/FrappeList.native.js.map +1 -1
- package/dist/jsx/FrappeListView.mjs +7 -13
- package/dist/jsx/FrappeListView.mjs.map +1 -1
- package/dist/jsx/FrappeListView.native.js +13 -12
- package/dist/jsx/FrappeListView.native.js.map +1 -1
- package/dist/jsx/FrappeProvider.mjs +5 -1
- package/dist/jsx/FrappeProvider.mjs.map +1 -1
- package/dist/jsx/FrappeProvider.native.js +5 -1
- package/dist/jsx/FrappeProvider.native.js.map +1 -1
- package/dist/jsx/FrappeReportBuilder.mjs +2 -0
- package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
- package/dist/jsx/FrappeReportBuilder.native.js +2 -0
- package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
- package/dist/jsx/FrappeReportView.mjs +26 -49
- package/dist/jsx/FrappeReportView.mjs.map +1 -1
- package/dist/jsx/FrappeReportView.native.js +35 -50
- package/dist/jsx/FrappeReportView.native.js.map +1 -1
- package/dist/jsx/FrappeTable.mjs +107 -33
- package/dist/jsx/FrappeTable.mjs.map +1 -1
- package/dist/jsx/FrappeTable.native.js +140 -42
- package/dist/jsx/FrappeTable.native.js.map +1 -1
- package/dist/jsx/FrappeTreeView.mjs +3 -1
- package/dist/jsx/FrappeTreeView.mjs.map +1 -1
- package/dist/jsx/FrappeTreeView.native.js +3 -1
- package/dist/jsx/FrappeTreeView.native.js.map +1 -1
- package/dist/jsx/SyncErrorNotice.mjs +51 -0
- package/dist/jsx/SyncErrorNotice.mjs.map +1 -0
- package/dist/jsx/SyncErrorNotice.native.js +92 -0
- package/dist/jsx/SyncErrorNotice.native.js.map +1 -0
- package/dist/jsx/commentActions.mjs +95 -0
- package/dist/jsx/commentActions.mjs.map +1 -0
- package/dist/jsx/commentActions.native.js +129 -0
- package/dist/jsx/commentActions.native.js.map +1 -0
- package/dist/jsx/components/attachments.mjs +2 -0
- package/dist/jsx/components/attachments.mjs.map +1 -1
- package/dist/jsx/components/attachments.native.js +2 -0
- package/dist/jsx/components/attachments.native.js.map +1 -1
- package/dist/jsx/components/tags.mjs +5 -0
- package/dist/jsx/components/tags.mjs.map +1 -1
- package/dist/jsx/components/tags.native.js +5 -0
- package/dist/jsx/components/tags.native.js.map +1 -1
- package/dist/jsx/components/timeline.mjs +31 -4
- package/dist/jsx/components/timeline.mjs.map +1 -1
- package/dist/jsx/components/timeline.native.js +33 -2
- package/dist/jsx/components/timeline.native.js.map +1 -1
- package/dist/jsx/desk/DeskFormSidebar.mjs +2 -0
- package/dist/jsx/desk/DeskFormSidebar.mjs.map +1 -1
- package/dist/jsx/desk/DeskFormSidebar.native.js +2 -0
- package/dist/jsx/desk/DeskFormSidebar.native.js.map +1 -1
- package/dist/jsx/fieldRegistry.mjs +2 -0
- package/dist/jsx/fieldRegistry.mjs.map +1 -1
- package/dist/jsx/fieldRegistry.native.js +2 -0
- package/dist/jsx/fieldRegistry.native.js.map +1 -1
- package/dist/jsx/fields/Date/index.mjs +73 -9
- package/dist/jsx/fields/Date/index.mjs.map +1 -1
- package/dist/jsx/fields/Date/index.native.js +325 -37
- package/dist/jsx/fields/Date/index.native.js.map +1 -1
- package/dist/jsx/fields/Date/types.mjs +2 -0
- package/dist/jsx/fields/Date/types.mjs.map +1 -0
- package/dist/jsx/fields/Date/types.native.js +21 -0
- package/dist/jsx/fields/Date/types.native.js.map +1 -0
- package/dist/jsx/fields/Date/wire.mjs +4 -8
- package/dist/jsx/fields/Date/wire.mjs.map +1 -1
- package/dist/jsx/fields/Date/wire.native.js +4 -11
- package/dist/jsx/fields/Date/wire.native.js.map +1 -1
- package/dist/jsx/fields/DynamicLink.mjs +2 -0
- package/dist/jsx/fields/DynamicLink.mjs.map +1 -1
- package/dist/jsx/fields/DynamicLink.native.js +2 -0
- package/dist/jsx/fields/DynamicLink.native.js.map +1 -1
- package/dist/jsx/fields/FileUpload.mjs +2 -0
- package/dist/jsx/fields/FileUpload.mjs.map +1 -1
- package/dist/jsx/fields/FileUpload.native.js +2 -0
- package/dist/jsx/fields/FileUpload.native.js.map +1 -1
- package/dist/jsx/fields/TableMultiSelect.mjs +7 -2
- package/dist/jsx/fields/TableMultiSelect.mjs.map +1 -1
- package/dist/jsx/fields/TableMultiSelect.native.js +6 -1
- package/dist/jsx/fields/TableMultiSelect.native.js.map +1 -1
- package/dist/jsx/frappeStoryTarget.mjs +88 -2
- package/dist/jsx/frappeStoryTarget.mjs.map +1 -1
- package/dist/jsx/frappeStoryTarget.native.js +100 -0
- package/dist/jsx/frappeStoryTarget.native.js.map +1 -1
- package/dist/jsx/frappeUsers.mjs +2 -0
- package/dist/jsx/frappeUsers.mjs.map +1 -1
- package/dist/jsx/frappeUsers.native.js +2 -0
- package/dist/jsx/frappeUsers.native.js.map +1 -1
- package/dist/jsx/index.js +7 -3
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +7 -3
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +17 -4
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/liveErrors.mjs +5 -1
- package/dist/jsx/liveErrors.mjs.map +1 -1
- package/dist/jsx/liveErrors.native.js +5 -1
- package/dist/jsx/liveErrors.native.js.map +1 -1
- package/dist/jsx/useFrappeAggregate.mjs +13 -1
- package/dist/jsx/useFrappeAggregate.mjs.map +1 -1
- package/dist/jsx/useFrappeAggregate.native.js +14 -1
- package/dist/jsx/useFrappeAggregate.native.js.map +1 -1
- package/dist/jsx/useFrappeFacets.mjs +299 -0
- package/dist/jsx/useFrappeFacets.mjs.map +1 -0
- package/dist/jsx/useFrappeFacets.native.js +632 -0
- package/dist/jsx/useFrappeFacets.native.js.map +1 -0
- package/package.json +20 -20
- package/src/FormSidebar.stories.tsx +88 -10
- package/src/FrappeAssignment.tsx +2 -0
- package/src/FrappeCalendar.tsx +12 -7
- package/src/FrappeChartCard.tsx +9 -2
- package/src/FrappeDashboard.spec.tsx +95 -0
- package/src/FrappeDashboard.tsx +20 -41
- package/src/FrappeForm.stories.tsx +53 -46
- package/src/FrappeForm.tsx +11 -1
- package/src/FrappeGantt.tsx +12 -7
- package/src/FrappeImageView.tsx +11 -1
- package/src/FrappeKPICard.stories.tsx +4 -1
- package/src/FrappeKPICard.tsx +2 -0
- package/src/FrappeKanban.tsx +4 -0
- package/src/FrappeLikedBy.stories.tsx +26 -10
- package/src/FrappeLikedBy.tsx +2 -0
- package/src/FrappeList.tsx +11 -1
- package/src/FrappeListView.spec.tsx +144 -0
- package/src/FrappeListView.tsx +11 -23
- package/src/FrappeProvider.tsx +30 -2
- package/src/FrappeReportBuilder.tsx +2 -0
- package/src/FrappeReportView.spec.tsx +140 -16
- package/src/FrappeReportView.stories.tsx +12 -6
- package/src/FrappeReportView.tsx +62 -61
- package/src/FrappeTable.keepRows.spec.tsx +200 -0
- package/src/FrappeTable.retryStorm.spec.tsx +140 -0
- package/src/FrappeTable.spec.tsx +222 -0
- package/src/FrappeTable.tsx +211 -54
- package/src/FrappeTreeView.tsx +11 -1
- package/src/SyncErrorNotice.spec.tsx +60 -0
- package/src/SyncErrorNotice.tsx +81 -0
- package/src/commentActions.ts +157 -0
- package/src/components/attachments.tsx +2 -0
- package/src/components/tags.tsx +14 -1
- package/src/components/timeline.optimistic.spec.tsx +106 -0
- package/src/components/timeline.tsx +36 -4
- package/src/desk/DeskFormSidebar.tsx +2 -0
- package/src/fieldRegistry.ts +2 -0
- package/src/fields/Date/index.native.tsx +410 -0
- package/src/fields/Date/index.tsx +138 -57
- package/src/fields/Date/nativePresentation.spec.tsx +251 -0
- package/src/fields/Date/typeParity.spec.tsx +480 -0
- package/src/fields/Date/types.ts +53 -0
- package/src/fields/Date/wire.spec.ts +63 -1
- package/src/fields/Date/wire.ts +17 -17
- package/src/fields/DynamicLink.tsx +2 -0
- package/src/fields/FileUpload.tsx +2 -0
- package/src/fields/TableMultiSelect.tsx +6 -3
- package/src/frappeStoryTarget.tsx +104 -1
- package/src/frappeUsers.ts +2 -0
- package/src/index.spec.ts +62 -0
- package/src/index.ts +36 -2
- package/src/liveErrors.spec.ts +50 -0
- package/src/liveErrors.ts +11 -1
- package/src/useFrappeAggregate.ts +20 -1
- package/src/useFrappeFacets.spec.tsx +436 -0
- package/src/useFrappeFacets.ts +568 -0
- package/types/FrappeAssignment.d.ts.map +1 -1
- package/types/FrappeCalendar.d.ts.map +1 -1
- package/types/FrappeChartCard.d.ts +1 -2
- package/types/FrappeChartCard.d.ts.map +1 -1
- package/types/FrappeDashboard.d.ts +0 -1
- package/types/FrappeDashboard.d.ts.map +1 -1
- package/types/FrappeForm.d.ts.map +1 -1
- package/types/FrappeGantt.d.ts.map +1 -1
- package/types/FrappeKPICard.d.ts.map +1 -1
- package/types/FrappeKanban.d.ts.map +1 -1
- package/types/FrappeLikedBy.d.ts.map +1 -1
- package/types/FrappeList.d.ts.map +1 -1
- package/types/FrappeListView.d.ts.map +1 -1
- package/types/FrappeProvider.d.ts +18 -1
- package/types/FrappeProvider.d.ts.map +1 -1
- package/types/FrappeReportBuilder.d.ts.map +1 -1
- package/types/FrappeReportView.d.ts +22 -15
- package/types/FrappeReportView.d.ts.map +1 -1
- package/types/FrappeTable.d.ts +21 -2
- package/types/FrappeTable.d.ts.map +1 -1
- package/types/FrappeTreeView.d.ts.map +1 -1
- package/types/SyncErrorNotice.d.ts +38 -0
- package/types/SyncErrorNotice.d.ts.map +1 -0
- package/types/commentActions.d.ts +46 -0
- package/types/commentActions.d.ts.map +1 -0
- package/types/components/attachments.d.ts.map +1 -1
- package/types/components/tags.d.ts.map +1 -1
- package/types/components/timeline.d.ts.map +1 -1
- package/types/desk/DeskFormSidebar.d.ts.map +1 -1
- package/types/fieldRegistry.d.ts.map +1 -1
- package/types/fields/Date/index.d.ts +22 -12
- package/types/fields/Date/index.d.ts.map +1 -1
- package/types/fields/Date/index.native.d.ts +14 -0
- package/types/fields/Date/index.native.d.ts.map +1 -0
- package/types/fields/Date/types.d.ts +27 -0
- package/types/fields/Date/types.d.ts.map +1 -0
- package/types/fields/Date/wire.d.ts +13 -9
- package/types/fields/Date/wire.d.ts.map +1 -1
- package/types/fields/DynamicLink.d.ts.map +1 -1
- package/types/fields/FileUpload.d.ts.map +1 -1
- package/types/fields/TableMultiSelect.d.ts.map +1 -1
- package/types/frappeStoryTarget.d.ts +19 -1
- package/types/frappeStoryTarget.d.ts.map +1 -1
- package/types/frappeUsers.d.ts.map +1 -1
- package/types/index.d.ts +6 -2
- package/types/index.d.ts.map +1 -1
- package/types/liveErrors.d.ts +4 -0
- package/types/liveErrors.d.ts.map +1 -1
- package/types/useFrappeAggregate.d.ts +4 -0
- package/types/useFrappeAggregate.d.ts.map +1 -1
- package/types/useFrappeFacets.d.ts +143 -0
- package/types/useFrappeFacets.d.ts.map +1 -0
- package/dist/cjs/fields/Date/date.native.js +0 -277
- package/dist/cjs/fields/Date/date.native.js.map +0 -1
- package/dist/esm/fields/Date/date.native.js +0 -238
- package/dist/esm/fields/Date/date.native.js.map +0 -1
- package/dist/jsx/fields/Date/date.native.js +0 -277
- package/dist/jsx/fields/Date/date.native.js.map +0 -1
- package/src/fields/Date/date.native.tsx +0 -299
- package/types/fields/Date/date.native.d.ts +0 -19
- package/types/fields/Date/date.native.d.ts.map +0 -1
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
// through the house DataTable. These specs cover the pure payload
|
|
3
3
|
// normalization (dict/string columns, dict/array rows incl. the server's
|
|
4
4
|
// array-shaped total row), the fieldtype → DataTableColumn mapping
|
|
5
|
-
// (DG-TBL-01 align-by-type
|
|
5
|
+
// (DG-TBL-01 align-by-type on the typed meta channel), row activation via
|
|
6
|
+
// DataTable's first-class `onRowPress`/`rowLabel` (the replacement contract
|
|
7
|
+
// for the deleted per-cell pressable layer: one pressable row unit, one
|
|
8
|
+
// roving tab stop per LC-49, Enter AND Space per LC-50, honest row names
|
|
9
|
+
// per DG-A11Y-06), the filter → refetch flow (debounced runs,
|
|
6
10
|
// controlled/uncontrolled values, defaults seeding, refresh), and the honest
|
|
7
11
|
// empty / error states.
|
|
8
12
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
9
13
|
import { getDefaultColumnAlign } from "@multiplatform.one/table";
|
|
10
14
|
import { cleanup, fireEvent, screen, waitFor } from "@testing-library/react";
|
|
11
15
|
import i18next from "i18next";
|
|
12
|
-
import type { ReactElement } from "react";
|
|
13
16
|
import { initReactI18next } from "react-i18next";
|
|
14
17
|
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
|
15
18
|
import {
|
|
@@ -20,6 +23,7 @@ import {
|
|
|
20
23
|
normalizeReportRows,
|
|
21
24
|
reportColumnToDataTableColumn,
|
|
22
25
|
reportFieldtypeToTableField,
|
|
26
|
+
reportRowLabel,
|
|
23
27
|
scrubFieldname,
|
|
24
28
|
type FrappeReportColumn,
|
|
25
29
|
type FrappeReportRunResponse,
|
|
@@ -261,20 +265,25 @@ describe("reportColumnToDataTableColumn", () => {
|
|
|
261
265
|
expect(col.width).toBeUndefined();
|
|
262
266
|
});
|
|
263
267
|
|
|
264
|
-
it("
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
expect(
|
|
268
|
+
it("pins DG-TBL-01 alignment on the typed meta channel (numerics right)", () => {
|
|
269
|
+
const alignFor = (fieldtype: string) =>
|
|
270
|
+
reportColumnToDataTableColumn({ label: "X", fieldname: "x", fieldtype }).meta?.align;
|
|
271
|
+
expect(alignFor("Int")).toBe("right");
|
|
272
|
+
expect(alignFor("Float")).toBe("right");
|
|
273
|
+
expect(alignFor("Currency")).toBe("right");
|
|
274
|
+
expect(alignFor("Check")).toBe("center");
|
|
275
|
+
expect(alignFor("Data")).toBe("left");
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
describe("reportRowLabel", () => {
|
|
280
|
+
it("names rows by name, then title, and stays empty otherwise", () => {
|
|
281
|
+
expect(reportRowLabel({ name: "SINV-0001", title: "Acme" })).toBe("SINV-0001");
|
|
282
|
+
expect(reportRowLabel({ title: "Acme Corp" })).toBe("Acme Corp");
|
|
283
|
+
expect(reportRowLabel({ name: 42 })).toBe("42");
|
|
284
|
+
// Empty defers to DataTable's fallbacks (first cell → "Row N").
|
|
285
|
+
expect(reportRowLabel({ table: "tabDocField" })).toBe("");
|
|
286
|
+
expect(reportRowLabel({ name: "" })).toBe("");
|
|
278
287
|
});
|
|
279
288
|
});
|
|
280
289
|
|
|
@@ -325,6 +334,27 @@ describe("FrappeReportView", () => {
|
|
|
325
334
|
expect(await screen.findByText("Error running Broken Report")).toBeTruthy();
|
|
326
335
|
});
|
|
327
336
|
|
|
337
|
+
it("permission chrome does not render a python traceback", async () => {
|
|
338
|
+
const err = Object.assign(
|
|
339
|
+
new Error(
|
|
340
|
+
'["Traceback (most recent call last):\\n File "apps/frappe/frappe/app.py", line 120, in application',
|
|
341
|
+
),
|
|
342
|
+
{ status: 403 },
|
|
343
|
+
);
|
|
344
|
+
renderWithProviders(
|
|
345
|
+
<FrappeReportView
|
|
346
|
+
reportName="Database Storage Usage By Tables"
|
|
347
|
+
fixture={() => {
|
|
348
|
+
throw err;
|
|
349
|
+
}}
|
|
350
|
+
/>,
|
|
351
|
+
);
|
|
352
|
+
expect(
|
|
353
|
+
await screen.findByText("You do not have permission to run Database Storage Usage By Tables"),
|
|
354
|
+
).toBeTruthy();
|
|
355
|
+
expect(screen.queryByText(/Traceback/)).toBeNull();
|
|
356
|
+
});
|
|
357
|
+
|
|
328
358
|
it("re-runs the report when a filter value changes (filter → refetch)", async () => {
|
|
329
359
|
const loader = vi.fn(async ({ filters }: { filters: Record<string, unknown> }) => ({
|
|
330
360
|
columns: storageReport.columns,
|
|
@@ -415,3 +445,97 @@ describe("FrappeReportView", () => {
|
|
|
415
445
|
expect(await screen.findByText("Nothing to see here")).toBeTruthy();
|
|
416
446
|
});
|
|
417
447
|
});
|
|
448
|
+
|
|
449
|
+
// ---------------------------------------------------------------------------
|
|
450
|
+
// Row activation — DataTable onRowPress (the replacement contract for the
|
|
451
|
+
// deleted per-cell pressable layer)
|
|
452
|
+
// ---------------------------------------------------------------------------
|
|
453
|
+
|
|
454
|
+
const pressableReport: FrappeReportRunResponse = {
|
|
455
|
+
columns: [
|
|
456
|
+
{ label: "Name", fieldname: "name", fieldtype: "Link", options: "ToDo", width: 140 },
|
|
457
|
+
{ label: "Table", fieldname: "table", fieldtype: "Data", width: 200 },
|
|
458
|
+
{ label: "Size (MB)", fieldname: "size", fieldtype: "Float" },
|
|
459
|
+
],
|
|
460
|
+
result: [
|
|
461
|
+
{ name: "ROW-0001", table: "tabDocField", size: 3.09 },
|
|
462
|
+
{ name: "ROW-0002", table: "tabError Log", size: 0.89 },
|
|
463
|
+
{ name: "ROW-0003", table: "tabSeries", size: 0.02 },
|
|
464
|
+
],
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
function pressRows(container: HTMLElement): HTMLElement[] {
|
|
468
|
+
return Array.from(container.querySelectorAll('[data-row-press="true"]')) as HTMLElement[];
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function renderPressableReport(extraProps: Record<string, unknown> = {}) {
|
|
472
|
+
const onRowPress = vi.fn();
|
|
473
|
+
const result = renderWithProviders(
|
|
474
|
+
<FrappeReportView
|
|
475
|
+
reportName="Pressable Report"
|
|
476
|
+
fixture={pressableReport}
|
|
477
|
+
onRowPress={onRowPress}
|
|
478
|
+
tableProps={{ layout: "table", enablePagination: false }}
|
|
479
|
+
{...extraProps}
|
|
480
|
+
/>,
|
|
481
|
+
);
|
|
482
|
+
return { ...result, onRowPress };
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
describe("FrappeReportView onRowPress → DataTable onRowPress", () => {
|
|
486
|
+
it("activates on mouse click anywhere in the row and on Enter AND Space", async () => {
|
|
487
|
+
const { container, onRowPress } = renderPressableReport();
|
|
488
|
+
await screen.findByText("tabDocField");
|
|
489
|
+
const rows = pressRows(container);
|
|
490
|
+
expect(rows).toHaveLength(3);
|
|
491
|
+
|
|
492
|
+
// Mouse: any cell in the row is the row's press target (index keeps
|
|
493
|
+
// its full-row-set meaning).
|
|
494
|
+
fireEvent.click(screen.getByText("tabError Log"));
|
|
495
|
+
expect(onRowPress).toHaveBeenCalledTimes(1);
|
|
496
|
+
expect(onRowPress).toHaveBeenCalledWith(expect.objectContaining({ name: "ROW-0002" }), 1);
|
|
497
|
+
|
|
498
|
+
// Keyboard: Enter AND Space activate the focused row (LC-50).
|
|
499
|
+
rows[0].focus();
|
|
500
|
+
fireEvent.keyDown(rows[0], { key: "Enter" });
|
|
501
|
+
expect(onRowPress).toHaveBeenCalledTimes(2);
|
|
502
|
+
expect(onRowPress).toHaveBeenLastCalledWith(expect.objectContaining({ name: "ROW-0001" }), 0);
|
|
503
|
+
fireEvent.keyDown(rows[0], { key: " " });
|
|
504
|
+
expect(onRowPress).toHaveBeenCalledTimes(3);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
it("exposes ONE roving tab stop, names rows honestly, and keeps cells pressable-free", async () => {
|
|
508
|
+
const { container } = renderPressableReport();
|
|
509
|
+
await screen.findByText("tabDocField");
|
|
510
|
+
const rows = pressRows(container);
|
|
511
|
+
|
|
512
|
+
// LC-49: exactly one tab stop across the row set; arrows rove it.
|
|
513
|
+
expect(rows.map((row) => row.getAttribute("tabindex"))).toEqual(["0", "-1", "-1"]);
|
|
514
|
+
rows[0].focus();
|
|
515
|
+
fireEvent.keyDown(rows[0], { key: "ArrowDown" });
|
|
516
|
+
expect(document.activeElement).toBe(rows[1]);
|
|
517
|
+
|
|
518
|
+
// DG-A11Y-06: rows named by report identity (the name column).
|
|
519
|
+
expect(rows.map((row) => row.getAttribute("aria-label"))).toEqual([
|
|
520
|
+
"ROW-0001",
|
|
521
|
+
"ROW-0002",
|
|
522
|
+
"ROW-0003",
|
|
523
|
+
]);
|
|
524
|
+
|
|
525
|
+
// The per-cell pressable layer is GONE: no cell-level buttons or
|
|
526
|
+
// extra tab stops inside pressable rows.
|
|
527
|
+
for (const row of rows) {
|
|
528
|
+
expect(row.querySelectorAll('[role="button"]')).toHaveLength(0);
|
|
529
|
+
expect(row.querySelectorAll('[tabindex="0"]')).toHaveLength(0);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
it("falls back to the first visible cell for rows without a name/title column", async () => {
|
|
534
|
+
const { container } = renderPressableReport({ fixture: storageReport });
|
|
535
|
+
await screen.findByText("tabDocField");
|
|
536
|
+
expect(pressRows(container).map((row) => row.getAttribute("aria-label"))).toEqual([
|
|
537
|
+
"tabDocField",
|
|
538
|
+
"tabError Log",
|
|
539
|
+
]);
|
|
540
|
+
});
|
|
541
|
+
});
|
|
@@ -154,20 +154,26 @@ const storageReportFixture: FrappeReportRunResponse = {
|
|
|
154
154
|
add_total_row: true,
|
|
155
155
|
};
|
|
156
156
|
|
|
157
|
-
export const Live = () =>
|
|
158
|
-
|
|
157
|
+
export const Live = () =>
|
|
158
|
+
frappeStoriesLive ? (
|
|
159
|
+
<FrappeStoryProvider>
|
|
160
|
+
<FrappeReportView
|
|
161
|
+
reportName="Database Storage Usage By Tables"
|
|
162
|
+
onRowPress={action("onRowPress")}
|
|
163
|
+
/>
|
|
164
|
+
</FrappeStoryProvider>
|
|
165
|
+
) : (
|
|
159
166
|
<FrappeReportView
|
|
160
167
|
reportName="Database Storage Usage By Tables"
|
|
161
|
-
fixture={
|
|
168
|
+
fixture={storageReportFixture}
|
|
162
169
|
onRowPress={action("onRowPress")}
|
|
163
170
|
/>
|
|
164
|
-
|
|
165
|
-
);
|
|
171
|
+
);
|
|
166
172
|
Live.parameters = {
|
|
167
173
|
docs: {
|
|
168
174
|
description: {
|
|
169
175
|
story:
|
|
170
|
-
"Runs the stock 'Database Storage Usage By Tables' Script Report against the real bench through the Storybook proxy (
|
|
176
|
+
"Runs the stock 'Database Storage Usage By Tables' Script Report against the real bench through the Storybook proxy. A session without Report permission (a plain Desk User) sees the permission EmptyState — 'You do not have permission to run …' plus the server's sentence — not a python traceback; same honesty as QueryReportScreen Live. System Manager / Administrator sees the table. When the bench is down the story renders a fixture payload mirroring the same column contract.",
|
|
171
177
|
},
|
|
172
178
|
},
|
|
173
179
|
};
|
package/src/FrappeReportView.tsx
CHANGED
|
@@ -25,9 +25,13 @@
|
|
|
25
25
|
* anything else falls back to Data.
|
|
26
26
|
*
|
|
27
27
|
* Column mapping targets read-only display: fieldtypes map to DataTable
|
|
28
|
-
* `field` types
|
|
29
|
-
* (
|
|
30
|
-
*
|
|
28
|
+
* `field` types and cells format through the forms `FieldDisplay` pipeline
|
|
29
|
+
* (`readOnly`); the DG-TBL-01 align-by-type resolution (numerics right via
|
|
30
|
+
* `stepper`, Check centered via `checkbox`, text left) is pinned once per
|
|
31
|
+
* column on the typed `meta.align` channel. Row activation rides
|
|
32
|
+
* DataTable's first-class `onRowPress`/`rowLabel` contract — the whole row
|
|
33
|
+
* is ONE pressable unit (no per-cell pressable wrappers), named by the
|
|
34
|
+
* report row's `name`/`title` value when present.
|
|
31
35
|
*
|
|
32
36
|
* CSV/JSON export reuses `DataTableExport` from `@multiplatform.one/table`
|
|
33
37
|
* (no new export machinery), capability-gated like FrappeReportBuilder (B12):
|
|
@@ -35,7 +39,7 @@
|
|
|
35
39
|
*/
|
|
36
40
|
import { AsyncDebouncer, CallModule, HttpClient, type AuthConfig } from "@multiplatform.one/frappe";
|
|
37
41
|
import { AsyncBoundary } from "@multiplatform.one/components";
|
|
38
|
-
import { DatePicker,
|
|
42
|
+
import { DatePicker, Spinner } from "@multiplatform.one/forms";
|
|
39
43
|
import {
|
|
40
44
|
DataTable,
|
|
41
45
|
DataTableExport,
|
|
@@ -44,11 +48,11 @@ import {
|
|
|
44
48
|
type DataTableProps,
|
|
45
49
|
type DataTableField,
|
|
46
50
|
} from "@multiplatform.one/table";
|
|
47
|
-
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
51
|
+
import { formatCalendarDate, useResolvedKnobs } from "@multiplatform.one/theme";
|
|
48
52
|
import { ArrowClockwiseIcon } from "@phosphor-icons/react";
|
|
49
53
|
import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
|
50
54
|
import { useTranslation } from "react-i18next";
|
|
51
|
-
import { Button, Text,
|
|
55
|
+
import { Button, Text, XStack, YStack, type YStackProps } from "tamagui";
|
|
52
56
|
import { useFrappeConfig } from "./FrappeProvider";
|
|
53
57
|
import { Check } from "./fields/Check";
|
|
54
58
|
import { Data } from "./fields/Data";
|
|
@@ -269,62 +273,47 @@ export function reportFieldtypeToTableField(column: FrappeReportColumn): {
|
|
|
269
273
|
}
|
|
270
274
|
}
|
|
271
275
|
|
|
272
|
-
export interface ReportColumnMapOptions {
|
|
273
|
-
/** Row press handler — cells wrap in a pressable surface when set. */
|
|
274
|
-
onRowPress?: (row: FrappeReportRow, index: number) => void;
|
|
275
|
-
/** Current rows (index lookup for onRowPress). Read at press time. */
|
|
276
|
-
rowsRef?: { current: FrappeReportRow[] };
|
|
277
|
-
}
|
|
278
|
-
|
|
279
276
|
/**
|
|
280
277
|
* Map a normalized report column to a DataTableColumn.
|
|
281
278
|
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
279
|
+
* The column carries a plain `field` (FieldDisplay via the table's
|
|
280
|
+
* readOnly path) and pins its DG-TBL-01 alignment on the typed
|
|
281
|
+
* `meta.align` channel (numerics right, Check centered, text left) so the
|
|
282
|
+
* resolved alignment survives every downstream cell surface, custom
|
|
283
|
+
* renderers included (DataTable's aligned cell wrapper reads it).
|
|
287
284
|
*/
|
|
288
285
|
export function reportColumnToDataTableColumn(
|
|
289
286
|
column: FrappeReportColumn,
|
|
290
|
-
{ onRowPress, rowsRef }: ReportColumnMapOptions = {},
|
|
291
287
|
): DataTableColumn<FrappeReportRow> {
|
|
292
288
|
const { field, fieldProps } = reportFieldtypeToTableField(column);
|
|
293
289
|
const width = column.width != null ? Number(column.width) : undefined;
|
|
294
|
-
|
|
290
|
+
return {
|
|
295
291
|
id: column.fieldname,
|
|
296
292
|
accessorKey: column.fieldname,
|
|
297
293
|
header: column.label,
|
|
298
294
|
...(width && Number.isFinite(width) ? { width } : {}),
|
|
299
295
|
field,
|
|
300
296
|
...(fieldProps ? { fieldProps } : {}),
|
|
301
|
-
|
|
302
|
-
if (!onRowPress) return base;
|
|
303
|
-
return {
|
|
304
|
-
...base,
|
|
305
|
-
// Explicit align: the custom field would otherwise default to left.
|
|
306
|
-
align: getDefaultColumnAlign({ field, fieldProps }),
|
|
307
|
-
field: "custom",
|
|
308
|
-
fieldProps: {
|
|
309
|
-
...fieldProps,
|
|
310
|
-
render: (value: unknown, rowData: unknown) => (
|
|
311
|
-
<View
|
|
312
|
-
onPress={() => {
|
|
313
|
-
const rows = rowsRef?.current ?? [];
|
|
314
|
-
const row = rowData as FrappeReportRow;
|
|
315
|
-
onRowPress(row, rows.indexOf(row));
|
|
316
|
-
}}
|
|
317
|
-
cursor="pointer"
|
|
318
|
-
minWidth={0}
|
|
319
|
-
flexShrink={1}
|
|
320
|
-
>
|
|
321
|
-
<FieldDisplay field={field} value={value} fieldProps={fieldProps} />
|
|
322
|
-
</View>
|
|
323
|
-
),
|
|
324
|
-
},
|
|
297
|
+
meta: { align: getDefaultColumnAlign({ id: column.fieldname, field, fieldProps }) },
|
|
325
298
|
};
|
|
326
299
|
}
|
|
327
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Accessible row identity for pressable report rows (DG-A11Y-06): report
|
|
303
|
+
* rows carry no doctype meta, so identity is the conventional report
|
|
304
|
+
* identity columns — the linked document `name`, else `title`. An empty
|
|
305
|
+
* result defers to DataTable's own fallbacks (first visible cell value →
|
|
306
|
+
* positional "Row N").
|
|
307
|
+
*/
|
|
308
|
+
export function reportRowLabel(row: FrappeReportRow): string {
|
|
309
|
+
for (const key of ["name", "title"] as const) {
|
|
310
|
+
const value = row[key];
|
|
311
|
+
if (typeof value === "string" && value) return value;
|
|
312
|
+
if (typeof value === "number") return String(value);
|
|
313
|
+
}
|
|
314
|
+
return "";
|
|
315
|
+
}
|
|
316
|
+
|
|
328
317
|
/**
|
|
329
318
|
* Download capability gate for the built-in export path (B12 precedent from
|
|
330
319
|
* FrappeReportBuilder): `downloadBlob` rides a DOM anchor + object URL and
|
|
@@ -565,13 +554,6 @@ export function useFrappeReport(
|
|
|
565
554
|
// Filter bar
|
|
566
555
|
// ---------------------------------------------------------------------------
|
|
567
556
|
|
|
568
|
-
function toFrappeDate(date: Date): string {
|
|
569
|
-
const y = date.getFullYear();
|
|
570
|
-
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
571
|
-
const d = String(date.getDate()).padStart(2, "0");
|
|
572
|
-
return `${y}-${m}-${d}`;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
557
|
interface ReportFilterFieldProps {
|
|
576
558
|
def: FrappeReportFilterDef;
|
|
577
559
|
value: unknown;
|
|
@@ -602,7 +584,7 @@ function ReportFilterField({ def, value, onChange }: ReportFilterFieldProps) {
|
|
|
602
584
|
label={def.label}
|
|
603
585
|
compact
|
|
604
586
|
value={value == null ? null : String(value)}
|
|
605
|
-
onChange={(date) => onChange(date ?
|
|
587
|
+
onChange={(date) => onChange(date ? formatCalendarDate(date) : "")}
|
|
606
588
|
/>
|
|
607
589
|
);
|
|
608
590
|
case "Check":
|
|
@@ -648,7 +630,14 @@ export interface FrappeReportViewProps extends Omit<YStackProps, "children"> {
|
|
|
648
630
|
* only the desk evaluates.
|
|
649
631
|
*/
|
|
650
632
|
filterFields?: FrappeReportFilterDef[];
|
|
651
|
-
/**
|
|
633
|
+
/**
|
|
634
|
+
* Callback when a row is activated (row, index into the full row set).
|
|
635
|
+
* Rides DataTable's first-class `onRowPress` contract: the whole row is
|
|
636
|
+
* ONE pressable unit (no per-cell pressable wrappers), the row set
|
|
637
|
+
* exposes a single roving tab stop (LC-49), Enter AND Space activate
|
|
638
|
+
* the focused row (LC-50), and presses on inner interactive controls
|
|
639
|
+
* never fire the row press.
|
|
640
|
+
*/
|
|
652
641
|
onRowPress?: (row: FrappeReportRow, index: number) => void;
|
|
653
642
|
/** Fixture payload or loader — renders without any network. */
|
|
654
643
|
fixture?: FrappeReportFixture;
|
|
@@ -758,15 +747,10 @@ export function FrappeReportView({
|
|
|
758
747
|
}, [isControlled, reportName, filterDefs]);
|
|
759
748
|
|
|
760
749
|
const rows = payload?.rows ?? [];
|
|
761
|
-
const rowsRef = useRef<FrappeReportRow[]>(rows);
|
|
762
|
-
rowsRef.current = rows;
|
|
763
750
|
|
|
764
751
|
const columns = useMemo(
|
|
765
|
-
() =>
|
|
766
|
-
|
|
767
|
-
reportColumnToDataTableColumn(col, onRowPress ? { onRowPress, rowsRef } : {}),
|
|
768
|
-
),
|
|
769
|
-
[payload?.columns, onRowPress],
|
|
752
|
+
() => (payload?.columns ?? []).map((col) => reportColumnToDataTableColumn(col)),
|
|
753
|
+
[payload?.columns],
|
|
770
754
|
);
|
|
771
755
|
|
|
772
756
|
const exportColumns = useMemo(
|
|
@@ -881,7 +865,24 @@ export function FrappeReportView({
|
|
|
881
865
|
{...boundaryState}
|
|
882
866
|
/>
|
|
883
867
|
) : (
|
|
884
|
-
<ReportDataTable
|
|
868
|
+
<ReportDataTable
|
|
869
|
+
data={rows}
|
|
870
|
+
columns={columns}
|
|
871
|
+
readOnly
|
|
872
|
+
// onRowPress (the stable FrappeReportView prop) rides DataTable's
|
|
873
|
+
// first-class row activation: whole-row press target, roving tab
|
|
874
|
+
// stop (LC-49), Enter/Space activation (LC-50), inner interactive
|
|
875
|
+
// controls win. The forwarded index keeps its full-row-set
|
|
876
|
+
// meaning (DataTable reports page-local indexes). rowLabel names
|
|
877
|
+
// each pressable row by report identity. Both spread BEFORE
|
|
878
|
+
// {...tableProps} so an explicit consumer onRowPress/rowLabel
|
|
879
|
+
// still ejects (consumer-last house rule).
|
|
880
|
+
{...(onRowPress
|
|
881
|
+
? { onRowPress: (row: FrappeReportRow) => onRowPress(row, rows.indexOf(row)) }
|
|
882
|
+
: {})}
|
|
883
|
+
rowLabel={reportRowLabel}
|
|
884
|
+
{...tableProps}
|
|
885
|
+
/>
|
|
885
886
|
)}
|
|
886
887
|
</YStack>
|
|
887
888
|
);
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keep-rows-and-reconcile (the read arm of the ONE optimistic pattern):
|
|
3
|
+
* changing the table's filters must NOT tear the list down and re-render
|
|
4
|
+
* from empty. The current rows stay mounted, the new filter applies
|
|
5
|
+
* locally to what is already on screen (optimistic narrowing), and the
|
|
6
|
+
* real query reconciles when data returns — no blanking, no skeleton
|
|
7
|
+
* flash, no scroll reset.
|
|
8
|
+
*
|
|
9
|
+
* The named reproducer was the backoffice list: every filter toggle
|
|
10
|
+
* remounted FrappeTable (list keyed on a filter generation), resetting
|
|
11
|
+
* `hasLoadedOnce` and flashing the AsyncBoundary skeleton for the full
|
|
12
|
+
* round-trip (~1.6s of blank list against the live bench). These specs pin
|
|
13
|
+
* the replacement contract at the FrappeTable layer: the `filters` prop
|
|
14
|
+
* adopts in place (content-keyed), held rows render while the swapped
|
|
15
|
+
* collection loads, and the loading boundary is reserved for the FIRST
|
|
16
|
+
* load only.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { InMemoryFixtureProvider } from "@multiplatform.one/frappe";
|
|
20
|
+
import type { DataTableColumn } from "@multiplatform.one/table";
|
|
21
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
22
|
+
import { cleanup, screen, waitFor } from "@testing-library/react";
|
|
23
|
+
import i18next from "i18next";
|
|
24
|
+
import { initReactI18next } from "react-i18next";
|
|
25
|
+
import { afterEach, beforeAll, describe, expect, it } from "vitest";
|
|
26
|
+
import { FrappeProvider } from "./FrappeProvider";
|
|
27
|
+
import { FrappeTable } from "./FrappeTable";
|
|
28
|
+
|
|
29
|
+
beforeAll(async () => {
|
|
30
|
+
if (!i18next.isInitialized) {
|
|
31
|
+
await i18next.use(initReactI18next).init({
|
|
32
|
+
lng: "en",
|
|
33
|
+
interpolation: { escapeValue: false },
|
|
34
|
+
resources: { en: { translation: {} } },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
cleanup();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
interface Customer {
|
|
44
|
+
name: string;
|
|
45
|
+
doctype: string;
|
|
46
|
+
customer_name: string;
|
|
47
|
+
status: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const customers: Customer[] = [
|
|
51
|
+
{ name: "CUST-001", doctype: "Customer", customer_name: "Acme Corp", status: "Active" },
|
|
52
|
+
{ name: "CUST-002", doctype: "Customer", customer_name: "Globex", status: "Active" },
|
|
53
|
+
{ name: "CUST-003", doctype: "Customer", customer_name: "Initech", status: "Lead" },
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const columns: DataTableColumn<Partial<Customer>>[] = [
|
|
57
|
+
{ id: "name", accessorKey: "name", header: "ID" },
|
|
58
|
+
{ id: "customer_name", accessorKey: "customer_name", header: "Customer Name" },
|
|
59
|
+
{ id: "status", accessorKey: "status", header: "Status" },
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const filterFields = [{ id: "status", label: "Status", type: "text" as const }];
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Fixture provider whose getList can be gated: while `gate` is set, list
|
|
66
|
+
* fetches wait on it — models the server round-trip a filter change causes,
|
|
67
|
+
* so the specs can assert what renders DURING the gap.
|
|
68
|
+
*/
|
|
69
|
+
class GatedFixtureProvider extends InMemoryFixtureProvider {
|
|
70
|
+
gate: Promise<void> | null = null;
|
|
71
|
+
|
|
72
|
+
override async getList<TDoc extends { name: string; doctype: string }>(
|
|
73
|
+
...args: Parameters<InMemoryFixtureProvider["getList"]>
|
|
74
|
+
) {
|
|
75
|
+
if (this.gate) await this.gate;
|
|
76
|
+
return super.getList(...args) as Promise<Partial<TDoc>[]>;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function renderTable(
|
|
81
|
+
fixtures: InMemoryFixtureProvider,
|
|
82
|
+
filters: Array<[keyof Customer & string, string, unknown]>,
|
|
83
|
+
) {
|
|
84
|
+
return renderWithProviders(
|
|
85
|
+
<FrappeProvider baseURL="https://frappetable-keeprows.spec.test" fixtures={fixtures}>
|
|
86
|
+
<FrappeTable<Customer>
|
|
87
|
+
doctype="Customer"
|
|
88
|
+
columns={columns}
|
|
89
|
+
filterableFields={filterFields}
|
|
90
|
+
filters={filters}
|
|
91
|
+
layout="table"
|
|
92
|
+
enablePagination={false}
|
|
93
|
+
/>
|
|
94
|
+
</FrappeProvider>,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function rerenderTable(
|
|
99
|
+
rerender: (ui: React.ReactElement) => void,
|
|
100
|
+
fixtures: InMemoryFixtureProvider,
|
|
101
|
+
filters: Array<[keyof Customer & string, string, unknown]>,
|
|
102
|
+
) {
|
|
103
|
+
rerender(
|
|
104
|
+
<FrappeProvider baseURL="https://frappetable-keeprows.spec.test" fixtures={fixtures}>
|
|
105
|
+
<FrappeTable<Customer>
|
|
106
|
+
doctype="Customer"
|
|
107
|
+
columns={columns}
|
|
108
|
+
filterableFields={filterFields}
|
|
109
|
+
filters={filters}
|
|
110
|
+
layout="table"
|
|
111
|
+
enablePagination={false}
|
|
112
|
+
/>
|
|
113
|
+
</FrappeProvider>,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
describe("FrappeTable keep-rows-and-reconcile on filter change", () => {
|
|
118
|
+
it("keeps current rows rendered and locally narrows them while the new query is in flight, then reconciles", async () => {
|
|
119
|
+
const fixtures = new GatedFixtureProvider({ Customer: customers });
|
|
120
|
+
const { rerender } = renderTable(fixtures, []);
|
|
121
|
+
await screen.findByText("Acme Corp");
|
|
122
|
+
expect(screen.getByText("Initech")).toBeTruthy();
|
|
123
|
+
|
|
124
|
+
// Gate the round-trip the filter change will cause.
|
|
125
|
+
let release: () => void = () => {};
|
|
126
|
+
fixtures.gate = new Promise<void>((resolve) => {
|
|
127
|
+
release = resolve;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
rerenderTable(rerender, fixtures, [["status", "=", "Active"]]);
|
|
131
|
+
|
|
132
|
+
// DURING the gap: the matching rows never left the screen, the filter
|
|
133
|
+
// already applied to what was cached locally, and no loading boundary
|
|
134
|
+
// replaced the table.
|
|
135
|
+
await waitFor(() => expect(screen.queryByText("Initech")).toBeNull());
|
|
136
|
+
expect(screen.getByText("Acme Corp")).toBeTruthy();
|
|
137
|
+
expect(screen.getByText("Globex")).toBeTruthy();
|
|
138
|
+
expect(screen.queryByText(/No Customer records found/i)).toBeNull();
|
|
139
|
+
|
|
140
|
+
// Reconcile: the server (fixture) result replaces the held rows.
|
|
141
|
+
release();
|
|
142
|
+
fixtures.gate = null;
|
|
143
|
+
await waitFor(() => {
|
|
144
|
+
expect(screen.getByText("Acme Corp")).toBeTruthy();
|
|
145
|
+
expect(screen.getByText("Globex")).toBeTruthy();
|
|
146
|
+
expect(screen.queryByText("Initech")).toBeNull();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("a filter that matches no current rows keeps the last-good rows mounted (no empty boundary)", async () => {
|
|
151
|
+
const fixtures = new GatedFixtureProvider({ Customer: customers });
|
|
152
|
+
const { rerender } = renderTable(fixtures, []);
|
|
153
|
+
await screen.findByText("Acme Corp");
|
|
154
|
+
|
|
155
|
+
let release: () => void = () => {};
|
|
156
|
+
fixtures.gate = new Promise<void>((resolve) => {
|
|
157
|
+
release = resolve;
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
rerenderTable(rerender, fixtures, [["status", "=", "Closed"]]);
|
|
161
|
+
|
|
162
|
+
// DURING the gap: local apply would empty the page — previous rows stay
|
|
163
|
+
// as placeholderData. The empty AsyncBoundary must not replace the table.
|
|
164
|
+
expect(screen.getByText("Acme Corp")).toBeTruthy();
|
|
165
|
+
expect(screen.getByText("Initech")).toBeTruthy();
|
|
166
|
+
expect(screen.queryByText(/No Customer records found/i)).toBeNull();
|
|
167
|
+
|
|
168
|
+
release();
|
|
169
|
+
fixtures.gate = null;
|
|
170
|
+
await waitFor(() => {
|
|
171
|
+
expect(screen.queryByText("Acme Corp")).toBeNull();
|
|
172
|
+
expect(screen.getByText(/No Customer records found/i)).toBeTruthy();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("clearing the filter keeps the narrowed rows rendered until the wider set reconciles in", async () => {
|
|
177
|
+
const fixtures = new GatedFixtureProvider({ Customer: customers });
|
|
178
|
+
const { rerender } = renderTable(fixtures, [["status", "=", "Active"]]);
|
|
179
|
+
await screen.findByText("Acme Corp");
|
|
180
|
+
expect(screen.queryByText("Initech")).toBeNull();
|
|
181
|
+
|
|
182
|
+
let release: () => void = () => {};
|
|
183
|
+
fixtures.gate = new Promise<void>((resolve) => {
|
|
184
|
+
release = resolve;
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
rerenderTable(rerender, fixtures, []);
|
|
188
|
+
|
|
189
|
+
// DURING the gap: last-good rows stay (no filter to narrow with).
|
|
190
|
+
expect(screen.getByText("Acme Corp")).toBeTruthy();
|
|
191
|
+
expect(screen.getByText("Globex")).toBeTruthy();
|
|
192
|
+
expect(screen.queryByText(/No Customer records found/i)).toBeNull();
|
|
193
|
+
|
|
194
|
+
release();
|
|
195
|
+
fixtures.gate = null;
|
|
196
|
+
// Reconcile: the wider set (including Initech) arrives.
|
|
197
|
+
await screen.findByText("Initech");
|
|
198
|
+
expect(screen.getByText("Acme Corp")).toBeTruthy();
|
|
199
|
+
});
|
|
200
|
+
});
|