@gravity-ui/dashkit 10.2.0 → 10.4.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.
Files changed (121) hide show
  1. package/README.md +127 -20
  2. package/build/cjs/components/DashKit/DashKit.d.ts +11 -1
  3. package/build/cjs/components/DashKit/DashKit.js +55 -7
  4. package/build/cjs/components/DashKit/DashKit.js.map +1 -1
  5. package/build/cjs/components/DashKit/__tests__/controlled-layout.test.d.ts +4 -0
  6. package/build/cjs/components/DashKit/__tests__/controlled-layout.test.js +196 -0
  7. package/build/cjs/components/DashKit/__tests__/controlled-layout.test.js.map +1 -0
  8. package/build/cjs/components/DashKit/__tests__/events.test.d.ts +1 -0
  9. package/build/cjs/components/DashKit/__tests__/events.test.js +227 -0
  10. package/build/cjs/components/DashKit/__tests__/events.test.js.map +1 -0
  11. package/build/cjs/components/DashKitView/DashKitView.d.ts +1 -1
  12. package/build/cjs/components/DashKitView/DashKitView.js +1 -3
  13. package/build/cjs/components/DashKitView/DashKitView.js.map +1 -1
  14. package/build/cjs/components/GridLayout/GroupLayout.js +2 -2
  15. package/build/cjs/components/GridLayout/GroupLayout.js.map +1 -1
  16. package/build/cjs/components/GridLayout/ReactGridLayout.d.ts +1 -0
  17. package/build/cjs/components/GridLayout/ReactGridLayout.js +37 -0
  18. package/build/cjs/components/GridLayout/ReactGridLayout.js.map +1 -1
  19. package/build/cjs/components/OverlayControls/OverlayControls.js +1 -1
  20. package/build/cjs/components/OverlayControls/OverlayControls.js.map +1 -1
  21. package/build/cjs/context/DashKitContext.d.ts +1 -0
  22. package/build/cjs/context/DashKitContext.js.map +1 -1
  23. package/build/cjs/context/DashkitOverlayControlsContext.d.ts +1 -1
  24. package/build/cjs/context/DashkitOverlayControlsContext.js +2 -2
  25. package/build/cjs/context/DashkitOverlayControlsContext.js.map +1 -1
  26. package/build/cjs/hocs/withContext.d.ts +2 -2
  27. package/build/cjs/hocs/withContext.js +61 -31
  28. package/build/cjs/hocs/withContext.js.map +1 -1
  29. package/build/cjs/package.json +1 -1
  30. package/build/cjs/plugins/Text/Text.js +1 -1
  31. package/build/cjs/plugins/Text/Text.js.map +1 -1
  32. package/build/cjs/shared/types/config.d.ts +2 -0
  33. package/build/cjs/shared/types/config.js +9 -0
  34. package/build/cjs/shared/types/config.js.map +1 -1
  35. package/build/cjs/typings/events.d.ts +34 -0
  36. package/build/cjs/typings/events.js +3 -0
  37. package/build/cjs/typings/events.js.map +1 -0
  38. package/build/cjs/typings/index.d.ts +1 -0
  39. package/build/cjs/typings/index.js +1 -0
  40. package/build/cjs/typings/index.js.map +1 -1
  41. package/build/cjs/utils/__tests__/get-layout-patches.test.d.ts +1 -0
  42. package/build/cjs/utils/__tests__/get-layout-patches.test.js +51 -0
  43. package/build/cjs/utils/__tests__/get-layout-patches.test.js.map +1 -0
  44. package/build/cjs/utils/enrichLayoutWithDefaults.d.ts +5 -0
  45. package/build/cjs/utils/enrichLayoutWithDefaults.js +29 -0
  46. package/build/cjs/utils/enrichLayoutWithDefaults.js.map +1 -0
  47. package/build/cjs/utils/events.d.ts +10 -0
  48. package/build/cjs/utils/events.js +34 -0
  49. package/build/cjs/utils/events.js.map +1 -0
  50. package/build/cjs/utils/get-layout-patches.d.ts +3 -0
  51. package/build/cjs/utils/get-layout-patches.js +23 -0
  52. package/build/cjs/utils/get-layout-patches.js.map +1 -0
  53. package/build/cjs/utils/index.d.ts +3 -0
  54. package/build/cjs/utils/index.js +3 -0
  55. package/build/cjs/utils/index.js.map +1 -1
  56. package/build/cjs/utils/update-manager.js +2 -2
  57. package/build/cjs/utils/update-manager.js.map +1 -1
  58. package/build/docs/INDEX.md +534 -0
  59. package/build/esm/components/DashKit/DashKit.d.ts +11 -1
  60. package/build/esm/components/DashKit/DashKit.js +54 -6
  61. package/build/esm/components/DashKit/DashKit.js.map +1 -1
  62. package/build/esm/components/DashKit/__tests__/controlled-layout.test.d.ts +4 -0
  63. package/build/esm/components/DashKit/__tests__/controlled-layout.test.js +193 -0
  64. package/build/esm/components/DashKit/__tests__/controlled-layout.test.js.map +1 -0
  65. package/build/esm/components/DashKit/__tests__/events.test.d.ts +1 -0
  66. package/build/esm/components/DashKit/__tests__/events.test.js +225 -0
  67. package/build/esm/components/DashKit/__tests__/events.test.js.map +1 -0
  68. package/build/esm/components/DashKitView/DashKitView.d.ts +1 -1
  69. package/build/esm/components/DashKitView/DashKitView.js +1 -3
  70. package/build/esm/components/DashKitView/DashKitView.js.map +1 -1
  71. package/build/esm/components/GridLayout/GroupLayout.js +2 -2
  72. package/build/esm/components/GridLayout/GroupLayout.js.map +1 -1
  73. package/build/esm/components/GridLayout/ReactGridLayout.d.ts +1 -0
  74. package/build/esm/components/GridLayout/ReactGridLayout.js +37 -0
  75. package/build/esm/components/GridLayout/ReactGridLayout.js.map +1 -1
  76. package/build/esm/components/OverlayControls/OverlayControls.js +2 -2
  77. package/build/esm/components/OverlayControls/OverlayControls.js.map +1 -1
  78. package/build/esm/context/DashKitContext.d.ts +1 -0
  79. package/build/esm/context/DashKitContext.js.map +1 -1
  80. package/build/esm/context/DashkitOverlayControlsContext.d.ts +1 -1
  81. package/build/esm/context/DashkitOverlayControlsContext.js +1 -1
  82. package/build/esm/context/DashkitOverlayControlsContext.js.map +1 -1
  83. package/build/esm/hocs/withContext.d.ts +2 -2
  84. package/build/esm/hocs/withContext.js +64 -34
  85. package/build/esm/hocs/withContext.js.map +1 -1
  86. package/build/esm/package.json +1 -1
  87. package/build/esm/plugins/Text/Text.js +1 -1
  88. package/build/esm/plugins/Text/Text.js.map +1 -1
  89. package/build/esm/shared/types/config.d.ts +2 -0
  90. package/build/esm/shared/types/config.js +8 -1
  91. package/build/esm/shared/types/config.js.map +1 -1
  92. package/build/esm/typings/events.d.ts +34 -0
  93. package/build/esm/typings/events.js +2 -0
  94. package/build/esm/typings/events.js.map +1 -0
  95. package/build/esm/typings/index.d.ts +1 -0
  96. package/build/esm/typings/index.js +1 -0
  97. package/build/esm/typings/index.js.map +1 -1
  98. package/build/esm/utils/__tests__/get-layout-patches.test.d.ts +1 -0
  99. package/build/esm/utils/__tests__/get-layout-patches.test.js +49 -0
  100. package/build/esm/utils/__tests__/get-layout-patches.test.js.map +1 -0
  101. package/build/esm/utils/enrichLayoutWithDefaults.d.ts +5 -0
  102. package/build/esm/utils/enrichLayoutWithDefaults.js +25 -0
  103. package/build/esm/utils/enrichLayoutWithDefaults.js.map +1 -0
  104. package/build/esm/utils/events.d.ts +10 -0
  105. package/build/esm/utils/events.js +30 -0
  106. package/build/esm/utils/events.js.map +1 -0
  107. package/build/esm/utils/get-layout-patches.d.ts +3 -0
  108. package/build/esm/utils/get-layout-patches.js +19 -0
  109. package/build/esm/utils/get-layout-patches.js.map +1 -0
  110. package/build/esm/utils/index.d.ts +3 -0
  111. package/build/esm/utils/index.js +3 -0
  112. package/build/esm/utils/index.js.map +1 -1
  113. package/build/esm/utils/update-manager.js +3 -3
  114. package/build/esm/utils/update-manager.js.map +1 -1
  115. package/package.json +6 -1
  116. package/build/cjs/hooks/useCalcLayout.d.ts +0 -3
  117. package/build/cjs/hooks/useCalcLayout.js +0 -40
  118. package/build/cjs/hooks/useCalcLayout.js.map +0 -1
  119. package/build/esm/hooks/useCalcLayout.d.ts +0 -3
  120. package/build/esm/hooks/useCalcLayout.js +0 -35
  121. package/build/esm/hooks/useCalcLayout.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"DashKit.js","sourceRoot":"../../../../src","sources":["components/DashKit/DashKit.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\n\nimport noop from 'lodash/noop';\nimport pick from 'lodash/pick';\n\nimport {DEFAULT_GROUP, DEFAULT_NAMESPACE} from '../../constants';\nimport {DashKitDnDContext} from '../../context';\nimport type {\n Config,\n ConfigItem,\n ConfigLayout,\n GlobalParams,\n ItemDropProps,\n ItemsStateAndParams,\n} from '../../shared';\nimport type {\n AddConfigItem,\n AddNewItemOptions,\n ContextProps,\n DashKitGroup,\n GridReflowOptions,\n ItemManipulationCallback,\n MenuItem,\n Plugin,\n ReactGridLayoutProps,\n SetConfigItem,\n Settings,\n SettingsProps,\n} from '../../typings';\nimport {RegisterManager, UpdateManager, reflowLayout} from '../../utils';\nimport {DashKitDnDWrapper} from '../DashKitDnDWrapper/DashKitDnDWrapper';\nimport DashKitView from '../DashKitView/DashKitView';\nimport GridLayout from '../GridLayout/GridLayout';\nimport type {OverlayControlItem, PreparedCopyItemOptions} from '../OverlayControls/OverlayControls';\n\ninterface DashKitGeneralProps {\n config: Config;\n editMode: boolean;\n draggableHandleClassName?: string;\n overlayControls?: Record<string, OverlayControlItem[]> | null;\n overlayMenuItems?: MenuItem[] | null;\n}\n\ninterface DashKitDefaultProps {\n defaultGlobalParams: GlobalParams;\n globalParams: GlobalParams;\n itemsStateAndParams: ItemsStateAndParams;\n settings: SettingsProps;\n context: ContextProps;\n noOverlay: boolean;\n focusable?: boolean;\n groups?: DashKitGroup[];\n\n onItemEdit: (item: ConfigItem) => void;\n onChange: (data: {\n config: Config;\n itemsStateAndParams: ItemsStateAndParams;\n groups?: DashKitGroup[];\n }) => void;\n\n onDrop: (dropProps: ItemDropProps) => void;\n\n onItemMountChange?: (item: ConfigItem, state: {isAsync: boolean; isMounted: boolean}) => void;\n onItemRender?: (item: ConfigItem) => void;\n\n getPreparedCopyItemOptions?: (options: PreparedCopyItemOptions) => PreparedCopyItemOptions;\n onCopyFulfill?: (error: null | Error, data?: PreparedCopyItemOptions) => void;\n\n onItemFocus?: (item: ConfigItem) => void;\n onItemBlur?: (item: ConfigItem) => void;\n\n onDragStart?: ItemManipulationCallback;\n onDrag?: ItemManipulationCallback;\n onDragStop?: ItemManipulationCallback;\n onResizeStart?: ItemManipulationCallback;\n onResize?: ItemManipulationCallback;\n onResizeStop?: ItemManipulationCallback;\n}\n\nexport interface DashKitProps extends DashKitGeneralProps, Partial<DashKitDefaultProps> {}\n\ntype DashKitInnerProps = DashKitGeneralProps & DashKitDefaultProps;\n\nconst registerManager = new RegisterManager();\n\nconst getReflowProps = (props: ReactGridLayoutProps): GridReflowOptions =>\n Object.assign(\n {compactType: 'vertical', cols: 36},\n pick(props, 'cols', 'maxRows', 'compactType'),\n );\n\nconst getReflowGroupsConfig = (groups: DashKitGroup[] = []) => {\n const defaultGridProps = getReflowProps(registerManager.gridLayout);\n\n return {\n defaultProps: defaultGridProps,\n groups: groups.reduce<Record<string, GridReflowOptions>>((memo, g) => {\n const groupId = g.id || DEFAULT_GROUP;\n memo[groupId] = g.gridProperties\n ? getReflowProps(g.gridProperties(defaultGridProps))\n : defaultGridProps;\n\n return memo;\n }, {}),\n };\n};\n\nexport class DashKit extends React.PureComponent<DashKitInnerProps> {\n static defaultProps: DashKitDefaultProps = {\n onItemEdit: noop,\n onChange: noop,\n onDrop: noop,\n defaultGlobalParams: {},\n globalParams: {},\n itemsStateAndParams: {},\n settings: {\n autoupdateInterval: 0,\n silentLoading: false,\n },\n context: {},\n noOverlay: false,\n focusable: false,\n };\n\n static contextType = DashKitDnDContext;\n\n static registerPlugins(...plugins: Plugin[]) {\n plugins.forEach((plugin) => {\n registerManager.registerPlugin(plugin);\n });\n }\n\n static reloadPlugins(...plugins: Plugin[]) {\n plugins.forEach((plugin) => {\n registerManager.reloadPlugin(plugin);\n });\n }\n\n static setSettings(settings: Settings) {\n registerManager.setSettings(settings);\n }\n\n static setItem({\n item: setItem,\n namespace = DEFAULT_NAMESPACE,\n config,\n options = {},\n groups = [],\n }: {\n item: SetConfigItem;\n namespace?: string;\n config: Config;\n options?: Omit<AddNewItemOptions, 'reflowLayoutOptions'>;\n groups?: DashKitGroup[];\n }): Config {\n if (setItem.id) {\n return UpdateManager.editItem({\n item: setItem,\n namespace,\n config,\n options,\n });\n } else {\n const item = setItem as AddConfigItem;\n const layout = {...registerManager.getItem(item.type).defaultLayout};\n\n const reflowLayoutOptions = getReflowGroupsConfig(groups);\n\n const copyItem = {...item};\n\n if (copyItem.layout) {\n Object.assign(layout, copyItem.layout);\n delete copyItem.layout;\n }\n\n return UpdateManager.addItem({\n item: copyItem,\n namespace,\n config,\n layout,\n options: {...options, reflowLayoutOptions},\n });\n }\n }\n\n static removeItem({\n id,\n config,\n itemsStateAndParams,\n }: {\n id: string;\n config: Config;\n itemsStateAndParams: ItemsStateAndParams;\n }): {config: Config; itemsStateAndParams: ItemsStateAndParams} {\n return UpdateManager.removeItem({id, config, itemsStateAndParams});\n }\n\n static reflowLayout({\n newLayoutItem,\n layout,\n groups,\n }: {\n newLayoutItem?: ConfigLayout;\n layout: ConfigLayout[];\n groups?: DashKitGroup[];\n }) {\n return reflowLayout({\n newLayoutItem,\n layout,\n reflowLayoutOptions: getReflowGroupsConfig(groups),\n });\n }\n\n metaRef = React.createRef<GridLayout>();\n\n render() {\n const content = (\n <DashKitView registerManager={registerManager} ref={this.metaRef} {...this.props} />\n );\n\n if (!this.context && this.props.groups) {\n return <DashKitDnDWrapper>{content}</DashKitDnDWrapper>;\n }\n\n return content;\n }\n\n getItemsMeta() {\n return this.metaRef.current?.getItemsMeta() ?? [];\n }\n\n reloadItems(options?: {targetIds?: string[]; force?: boolean}) {\n this.metaRef.current?.reloadItems(options);\n }\n}\n"]}
1
+ {"version":3,"file":"DashKit.js","sourceRoot":"../../../../src","sources":["components/DashKit/DashKit.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\n\nimport noop from 'lodash/noop';\nimport pick from 'lodash/pick';\n\nimport {DEFAULT_GROUP, DEFAULT_NAMESPACE} from '../../constants';\nimport {DashKitDnDContext} from '../../context';\nimport type {\n Config,\n ConfigItem,\n ConfigLayout,\n GlobalParams,\n ItemDropProps,\n ItemsStateAndParams,\n} from '../../shared';\nimport type {\n AddConfigItem,\n AddNewItemOptions,\n ContextProps,\n DashKitEventHandler,\n DashKitEventMap,\n DashKitEventName,\n DashKitGroup,\n GridReflowOptions,\n ItemManipulationCallback,\n MenuItem,\n Plugin,\n ReactGridLayoutProps,\n SetConfigItem,\n Settings,\n SettingsProps,\n} from '../../typings';\nimport {RegisterManager, UpdateManager, reflowLayout} from '../../utils';\nimport {DashKitDnDWrapper} from '../DashKitDnDWrapper/DashKitDnDWrapper';\nimport DashKitView from '../DashKitView/DashKitView';\nimport GridLayout from '../GridLayout/GridLayout';\nimport type {OverlayControlItem, PreparedCopyItemOptions} from '../OverlayControls/OverlayControls';\n\ninterface DashKitGeneralProps {\n config: Config;\n editMode: boolean;\n draggableHandleClassName?: string;\n overlayControls?: Record<string, OverlayControlItem[]> | null;\n overlayMenuItems?: MenuItem[] | null;\n}\n\ninterface DashKitDefaultProps {\n defaultGlobalParams: GlobalParams;\n globalParams: GlobalParams;\n itemsStateAndParams: ItemsStateAndParams;\n settings: SettingsProps;\n context: ContextProps;\n noOverlay: boolean;\n focusable?: boolean;\n groups?: DashKitGroup[];\n\n onItemEdit: (item: ConfigItem) => void;\n onChange: (data: {\n config: Config;\n itemsStateAndParams: ItemsStateAndParams;\n groups?: DashKitGroup[];\n }) => void;\n\n onDrop: (dropProps: ItemDropProps) => void;\n\n onItemMountChange?: (item: ConfigItem, state: {isAsync: boolean; isMounted: boolean}) => void;\n onItemRender?: (item: ConfigItem) => void;\n\n getPreparedCopyItemOptions?: (options: PreparedCopyItemOptions) => PreparedCopyItemOptions;\n onCopyFulfill?: (error: null | Error, data?: PreparedCopyItemOptions) => void;\n\n onItemFocus?: (item: ConfigItem) => void;\n onItemBlur?: (item: ConfigItem) => void;\n\n onDragStart?: ItemManipulationCallback;\n onDrag?: ItemManipulationCallback;\n onDragStop?: ItemManipulationCallback;\n onResizeStart?: ItemManipulationCallback;\n onResize?: ItemManipulationCallback;\n onResizeStop?: ItemManipulationCallback;\n}\n\nexport interface DashKitProps extends DashKitGeneralProps, Partial<DashKitDefaultProps> {}\n\ntype DashKitInnerProps = DashKitGeneralProps & DashKitDefaultProps;\n\nconst registerManager = new RegisterManager();\n\n/**\n * @internal Not part of the public API. Do not use in production code.\n * Module-scoped symbol — not accessible to external consumers holding a ref,\n * preventing synthetic event injection via instance._emit.\n */\nexport const _emitSymbol = Symbol('DashKit._emit');\n\nconst getReflowProps = (props: ReactGridLayoutProps): GridReflowOptions =>\n Object.assign(\n {compactType: 'vertical', cols: 36},\n pick(props, 'cols', 'maxRows', 'compactType'),\n );\n\nconst getReflowGroupsConfig = (groups: DashKitGroup[] = []) => {\n const defaultGridProps = getReflowProps(registerManager.gridLayout);\n\n return {\n defaultProps: defaultGridProps,\n groups: groups.reduce<Record<string, GridReflowOptions>>((memo, g) => {\n const groupId = g.id || DEFAULT_GROUP;\n memo[groupId] = g.gridProperties\n ? getReflowProps(g.gridProperties(defaultGridProps))\n : defaultGridProps;\n\n return memo;\n }, {}),\n };\n};\n\nexport class DashKit extends React.PureComponent<DashKitInnerProps> {\n static defaultProps: DashKitDefaultProps = {\n onItemEdit: noop,\n onChange: noop,\n onDrop: noop,\n defaultGlobalParams: {},\n globalParams: {},\n itemsStateAndParams: {},\n settings: {\n autoupdateInterval: 0,\n silentLoading: false,\n },\n context: {},\n noOverlay: false,\n focusable: false,\n };\n\n static contextType = DashKitDnDContext;\n\n static registerPlugins(...plugins: Plugin[]) {\n plugins.forEach((plugin) => {\n registerManager.registerPlugin(plugin);\n });\n }\n\n static reloadPlugins(...plugins: Plugin[]) {\n plugins.forEach((plugin) => {\n registerManager.reloadPlugin(plugin);\n });\n }\n\n static setSettings(settings: Settings) {\n registerManager.setSettings(settings);\n }\n\n static setItem({\n item: setItem,\n namespace = DEFAULT_NAMESPACE,\n config,\n options = {},\n groups = [],\n }: {\n item: SetConfigItem;\n namespace?: string;\n config: Config;\n options?: Omit<AddNewItemOptions, 'reflowLayoutOptions'>;\n groups?: DashKitGroup[];\n }): Config {\n if (setItem.id) {\n return UpdateManager.editItem({\n item: setItem,\n namespace,\n config,\n options,\n });\n } else {\n const item = setItem as AddConfigItem;\n const registerManagerItem = registerManager.getItem(item.type);\n\n const layout = {...registerManagerItem.defaultLayout};\n\n const reflowLayoutOptions = getReflowGroupsConfig(groups);\n\n const copyItem = {...item};\n\n if (copyItem.layout) {\n Object.assign(layout, copyItem.layout);\n delete copyItem.layout;\n }\n\n return UpdateManager.addItem({\n item: copyItem,\n namespace,\n config,\n layout,\n options: {...options, reflowLayoutOptions},\n });\n }\n }\n\n static removeItem({\n id,\n config,\n itemsStateAndParams,\n }: {\n id: string;\n config: Config;\n itemsStateAndParams: ItemsStateAndParams;\n }): {config: Config; itemsStateAndParams: ItemsStateAndParams} {\n return UpdateManager.removeItem({id, config, itemsStateAndParams});\n }\n\n static reflowLayout({\n newLayoutItem,\n layout,\n groups,\n }: {\n newLayoutItem?: ConfigLayout;\n layout: ConfigLayout[];\n groups?: DashKitGroup[];\n }) {\n return reflowLayout({\n newLayoutItem,\n layout,\n reflowLayoutOptions: getReflowGroupsConfig(groups),\n });\n }\n\n metaRef = React.createRef<GridLayout>();\n\n private _handlers = new Map<string, Set<(...args: any[]) => void>>();\n\n render() {\n const content = (\n <DashKitView\n registerManager={registerManager}\n ref={this.metaRef}\n {...this.props}\n emitDashKitEvent={this[_emitSymbol]}\n />\n );\n\n if (!this.context && this.props.groups) {\n return <DashKitDnDWrapper>{content}</DashKitDnDWrapper>;\n }\n\n return content;\n }\n\n getItemsMeta() {\n return this.metaRef.current?.getItemsMeta() ?? [];\n }\n\n reloadItems(options?: {targetIds?: string[]; force?: boolean}) {\n this.metaRef.current?.reloadItems(options);\n }\n\n /**\n * @experimental This API can change in minor releases.\n * @param eventName Event name.\n * @param handler Event handler.\n * @returns Unsubscribe callback.\n */\n on<T extends DashKitEventName>(eventName: T, handler: DashKitEventHandler<T>): () => void {\n if (!this._handlers.has(eventName)) {\n this._handlers.set(eventName, new Set());\n }\n this._handlers.get(eventName)?.add(handler);\n return () => this._off(eventName, handler);\n }\n\n [_emitSymbol] = <T extends DashKitEventName>(eventName: T, event: DashKitEventMap[T]): void => {\n const handlers = this._handlers.get(eventName);\n if (!handlers) {\n return;\n }\n for (const handler of handlers) {\n try {\n (handler as DashKitEventHandler<T>)(event);\n } catch (e) {\n // eslint-disable-next-line no-console\n console.error('DashKit: event handler error', e);\n }\n }\n };\n\n private _off<T extends DashKitEventName>(eventName: T, handler: DashKitEventHandler<T>): void {\n const handlers = this._handlers.get(eventName);\n if (handlers) {\n handlers.delete(handler);\n if (handlers.size === 0) {\n this._handlers.delete(eventName);\n }\n }\n }\n}\n"]}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+ export {};
@@ -0,0 +1,193 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+ jest.mock('@gravity-ui/icons', () => ({
5
+ Ellipsis: 'ellipsis',
6
+ Gear: 'gear',
7
+ Xmark: 'xmark',
8
+ }));
9
+ jest.mock('@gravity-ui/uikit', () => {
10
+ const ReactMock = require('react');
11
+ return {
12
+ Button: ({ children }) => ReactMock.createElement('button', null, children),
13
+ DropdownMenu: () => null,
14
+ Icon: () => null,
15
+ };
16
+ });
17
+ jest.mock('../../GridLayout/GridLayout', () => {
18
+ const ReactMock = require('react');
19
+ return ReactMock.forwardRef(() => null);
20
+ });
21
+ import React from 'react';
22
+ import { act, render } from '@testing-library/react';
23
+ import { DashKitContext } from "../../../context/index.js";
24
+ import { withContext } from "../../../hocs/withContext.js";
25
+ import { RegisterManager } from "../../../utils/index.js";
26
+ import { DashKit, _emitSymbol } from "../DashKit.js";
27
+ const PLUGIN_TYPE = 'controlled-layout-test';
28
+ const createConfig = (x) => ({
29
+ salt: 'test',
30
+ counter: 1,
31
+ items: [{ id: 'item1', type: PLUGIN_TYPE, namespace: 'default', data: {} }],
32
+ layout: [{ i: 'item1', x, y: 0, w: 2, h: 2 }],
33
+ aliases: {},
34
+ connections: [],
35
+ });
36
+ const createProps = ({ config, emitDashKitEvent, onChange = jest.fn(), }) => {
37
+ const registerManager = new RegisterManager();
38
+ registerManager.registerPlugin({
39
+ type: PLUGIN_TYPE,
40
+ defaultLayout: { x: 0, y: 0, w: 2, h: 2 },
41
+ renderer: () => null,
42
+ });
43
+ return {
44
+ config,
45
+ editMode: true,
46
+ layout: config.layout,
47
+ registerManager,
48
+ forwardedMetaRef: React.createRef(),
49
+ emitDashKitEvent,
50
+ itemsStateAndParams: {},
51
+ defaultGlobalParams: {},
52
+ globalParams: {},
53
+ context: {},
54
+ settings: {
55
+ autoupdateInterval: 0,
56
+ silentLoading: false,
57
+ },
58
+ noOverlay: false,
59
+ focusable: false,
60
+ onItemEdit: jest.fn(),
61
+ onChange,
62
+ onDrop: jest.fn(),
63
+ };
64
+ };
65
+ let captured = {};
66
+ const ContextCapture = () => {
67
+ captured = React.useContext(DashKitContext);
68
+ return null;
69
+ };
70
+ const TestComponent = withContext(ContextCapture);
71
+ describe('DashKit controlled layout strategy', () => {
72
+ beforeEach(() => {
73
+ captured = {};
74
+ });
75
+ it('Case 1: keeps internal layout after change event without config update, then applies external config update', () => {
76
+ const initialConfig = createConfig(0);
77
+ const nextInternalLayout = [{ i: 'item1', x: 1, y: 0, w: 2, h: 2 }];
78
+ const externalConfig = createConfig(3);
79
+ const dashkit = new DashKit(DashKit.defaultProps);
80
+ const onChange = jest.fn();
81
+ const onEventChange = jest.fn();
82
+ dashkit.on('change', onEventChange);
83
+ const { rerender } = render(React.createElement(TestComponent, Object.assign({}, createProps({
84
+ config: initialConfig,
85
+ emitDashKitEvent: dashkit[_emitSymbol],
86
+ onChange,
87
+ }))));
88
+ // Layout is now enriched with plugin defaults (minW, minH, etc)
89
+ expect(captured.layout).toMatchObject(initialConfig.layout);
90
+ expect(captured.layout[0]).toHaveProperty('minW');
91
+ expect(captured.layout[0]).toHaveProperty('minH');
92
+ act(() => {
93
+ captured.layoutChange(nextInternalLayout);
94
+ });
95
+ // Verify that change event was fired with correct layout
96
+ expect(onEventChange).toHaveBeenCalledTimes(1);
97
+ expect(onEventChange.mock.calls[0][0].layout).toEqual(nextInternalLayout);
98
+ expect(onChange).toHaveBeenCalledTimes(1);
99
+ // Now apply external config update
100
+ act(() => {
101
+ rerender(React.createElement(TestComponent, Object.assign({}, createProps({
102
+ config: externalConfig,
103
+ emitDashKitEvent: dashkit[_emitSymbol],
104
+ onChange,
105
+ }))));
106
+ });
107
+ // External props should win and layout should be updated
108
+ expect(captured.layout).toMatchObject(externalConfig.layout);
109
+ });
110
+ it('Case 2: uses on(change) without updating props - internal state persists', () => {
111
+ const initialConfig = createConfig(0);
112
+ const nextInternalLayout = [{ i: 'item1', x: 5, y: 0, w: 2, h: 2 }];
113
+ const dashkit = new DashKit(DashKit.defaultProps);
114
+ const onChange = jest.fn();
115
+ const onEventChange = jest.fn();
116
+ const capturedLayouts = [];
117
+ // Listen to change event but DON'T update props
118
+ dashkit.on('change', (event) => {
119
+ capturedLayouts.push(event.layout);
120
+ onEventChange(event);
121
+ });
122
+ const { rerender } = render(React.createElement(TestComponent, Object.assign({}, createProps({
123
+ config: initialConfig,
124
+ emitDashKitEvent: dashkit[_emitSymbol],
125
+ onChange,
126
+ }))));
127
+ // Initial layout should be enriched
128
+ expect(captured.layout).toMatchObject(initialConfig.layout);
129
+ // Trigger layout change
130
+ act(() => {
131
+ captured.layoutChange(nextInternalLayout);
132
+ });
133
+ // Event should fire with new layout
134
+ expect(onEventChange).toHaveBeenCalledTimes(1);
135
+ expect(capturedLayouts[0]).toEqual(nextInternalLayout);
136
+ // onChange callback fires (consumer COULD update props here, but doesn't in this case)
137
+ expect(onChange).toHaveBeenCalledTimes(1);
138
+ // Props remain unchanged - simulating consumer NOT updating props
139
+ act(() => {
140
+ rerender(React.createElement(TestComponent, Object.assign({}, createProps({
141
+ config: initialConfig,
142
+ emitDashKitEvent: dashkit[_emitSymbol],
143
+ onChange,
144
+ }))));
145
+ });
146
+ // Layout should still reflect the change (internal state wins)
147
+ // Note: we verify the event fired correctly
148
+ expect(onEventChange).toHaveBeenCalledTimes(1);
149
+ expect(capturedLayouts[0]).toMatchObject(nextInternalLayout);
150
+ });
151
+ it('Case 3: alternates between props.layout updates and on(change) events', () => {
152
+ const config1 = createConfig(0);
153
+ const config2 = createConfig(2);
154
+ const internalChange = [{ i: 'item1', x: 5, y: 0, w: 2, h: 2 }];
155
+ const config3 = createConfig(8);
156
+ const dashkit = new DashKit(DashKit.defaultProps);
157
+ const onChange = jest.fn();
158
+ const onEventChange = jest.fn();
159
+ dashkit.on('change', onEventChange);
160
+ const { rerender } = render(React.createElement(TestComponent, Object.assign({}, createProps({
161
+ config: config1,
162
+ emitDashKitEvent: dashkit[_emitSymbol],
163
+ onChange,
164
+ }))));
165
+ // Start with config1
166
+ expect(captured.layout).toMatchObject(config1.layout);
167
+ // Update via props (external update)
168
+ act(() => {
169
+ rerender(React.createElement(TestComponent, Object.assign({}, createProps({
170
+ config: config2,
171
+ emitDashKitEvent: dashkit[_emitSymbol],
172
+ onChange,
173
+ }))));
174
+ });
175
+ expect(captured.layout).toMatchObject(config2.layout);
176
+ // Update via internal change (user drag)
177
+ act(() => {
178
+ captured.layoutChange(internalChange);
179
+ });
180
+ expect(onEventChange).toHaveBeenCalledTimes(1);
181
+ expect(onEventChange.mock.calls[0][0].layout).toEqual(internalChange);
182
+ // Update via props again (external update wins)
183
+ act(() => {
184
+ rerender(React.createElement(TestComponent, Object.assign({}, createProps({
185
+ config: config3,
186
+ emitDashKitEvent: dashkit[_emitSymbol],
187
+ onChange,
188
+ }))));
189
+ });
190
+ expect(captured.layout).toMatchObject(config3.layout);
191
+ });
192
+ });
193
+ //# sourceMappingURL=controlled-layout.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controlled-layout.test.js","sourceRoot":"../../../../../src","sources":["components/DashKit/__tests__/controlled-layout.test.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * @jest-environment jsdom\n */\n\njest.mock('@gravity-ui/icons', () => ({\n Ellipsis: 'ellipsis',\n Gear: 'gear',\n Xmark: 'xmark',\n}));\n\njest.mock('@gravity-ui/uikit', () => {\n const ReactMock = require('react');\n\n return {\n Button: ({children}: {children?: unknown}) =>\n ReactMock.createElement('button', null, children),\n DropdownMenu: () => null,\n Icon: () => null,\n };\n});\n\njest.mock('../../GridLayout/GridLayout', () => {\n const ReactMock = require('react');\n\n return ReactMock.forwardRef(() => null);\n});\n\nimport React from 'react';\n\nimport {act, render} from '@testing-library/react';\n\nimport {DashKitContext, type DashKitCtxShape} from '../../../context';\nimport {type DashKitWithContextProps, withContext} from '../../../hocs/withContext';\nimport type {Config} from '../../../shared';\nimport {RegisterManager} from '../../../utils';\nimport {DashKit, _emitSymbol} from '../DashKit';\n\nconst PLUGIN_TYPE = 'controlled-layout-test';\n\nconst createConfig = (x: number): Config => ({\n salt: 'test',\n counter: 1,\n items: [{id: 'item1', type: PLUGIN_TYPE, namespace: 'default', data: {}}],\n layout: [{i: 'item1', x, y: 0, w: 2, h: 2}],\n aliases: {},\n connections: [],\n});\n\nconst createProps = ({\n config,\n emitDashKitEvent,\n onChange = jest.fn(),\n}: {\n config: Config;\n emitDashKitEvent: DashKitWithContextProps['emitDashKitEvent'];\n onChange?: jest.Mock;\n}) => {\n const registerManager = new RegisterManager();\n registerManager.registerPlugin({\n type: PLUGIN_TYPE,\n defaultLayout: {x: 0, y: 0, w: 2, h: 2},\n renderer: () => null,\n });\n\n return {\n config,\n editMode: true,\n layout: config.layout,\n registerManager,\n forwardedMetaRef: React.createRef(),\n emitDashKitEvent,\n itemsStateAndParams: {},\n defaultGlobalParams: {},\n globalParams: {},\n context: {},\n settings: {\n autoupdateInterval: 0,\n silentLoading: false,\n },\n noOverlay: false,\n focusable: false,\n onItemEdit: jest.fn(),\n onChange,\n onDrop: jest.fn(),\n };\n};\n\nlet captured: DashKitCtxShape = {} as DashKitCtxShape;\n\nconst ContextCapture = () => {\n captured = React.useContext(DashKitContext);\n return null;\n};\n\nconst TestComponent = withContext(ContextCapture);\n\ndescribe('DashKit controlled layout strategy', () => {\n beforeEach(() => {\n captured = {} as DashKitCtxShape;\n });\n\n it('Case 1: keeps internal layout after change event without config update, then applies external config update', () => {\n const initialConfig = createConfig(0);\n const nextInternalLayout: Config['layout'] = [{i: 'item1', x: 1, y: 0, w: 2, h: 2}];\n const externalConfig = createConfig(3);\n const dashkit = new DashKit(\n DashKit.defaultProps as unknown as ConstructorParameters<typeof DashKit>[0],\n );\n const onChange = jest.fn();\n const onEventChange = jest.fn();\n\n dashkit.on('change', onEventChange);\n\n const {rerender} = render(\n <TestComponent\n {...createProps({\n config: initialConfig,\n emitDashKitEvent: dashkit[_emitSymbol],\n onChange,\n })}\n />,\n );\n\n // Layout is now enriched with plugin defaults (minW, minH, etc)\n expect(captured.layout).toMatchObject(initialConfig.layout);\n expect(captured.layout[0]).toHaveProperty('minW');\n expect(captured.layout[0]).toHaveProperty('minH');\n\n act(() => {\n captured.layoutChange(nextInternalLayout);\n });\n\n // Verify that change event was fired with correct layout\n expect(onEventChange).toHaveBeenCalledTimes(1);\n expect(onEventChange.mock.calls[0][0].layout).toEqual(nextInternalLayout);\n expect(onChange).toHaveBeenCalledTimes(1);\n\n // Now apply external config update\n act(() => {\n rerender(\n <TestComponent\n {...createProps({\n config: externalConfig,\n emitDashKitEvent: dashkit[_emitSymbol],\n onChange,\n })}\n />,\n );\n });\n\n // External props should win and layout should be updated\n expect(captured.layout).toMatchObject(externalConfig.layout);\n });\n\n it('Case 2: uses on(change) without updating props - internal state persists', () => {\n const initialConfig = createConfig(0);\n const nextInternalLayout: Config['layout'] = [{i: 'item1', x: 5, y: 0, w: 2, h: 2}];\n const dashkit = new DashKit(\n DashKit.defaultProps as unknown as ConstructorParameters<typeof DashKit>[0],\n );\n const onChange = jest.fn();\n const onEventChange = jest.fn();\n const capturedLayouts: Config['layout'][] = [];\n\n // Listen to change event but DON'T update props\n dashkit.on('change', (event) => {\n capturedLayouts.push(event.layout);\n onEventChange(event);\n });\n\n const {rerender} = render(\n <TestComponent\n {...createProps({\n config: initialConfig,\n emitDashKitEvent: dashkit[_emitSymbol],\n onChange,\n })}\n />,\n );\n\n // Initial layout should be enriched\n expect(captured.layout).toMatchObject(initialConfig.layout);\n\n // Trigger layout change\n act(() => {\n captured.layoutChange(nextInternalLayout);\n });\n\n // Event should fire with new layout\n expect(onEventChange).toHaveBeenCalledTimes(1);\n expect(capturedLayouts[0]).toEqual(nextInternalLayout);\n\n // onChange callback fires (consumer COULD update props here, but doesn't in this case)\n expect(onChange).toHaveBeenCalledTimes(1);\n\n // Props remain unchanged - simulating consumer NOT updating props\n act(() => {\n rerender(\n <TestComponent\n {...createProps({\n config: initialConfig,\n emitDashKitEvent: dashkit[_emitSymbol],\n onChange,\n })}\n />,\n );\n });\n\n // Layout should still reflect the change (internal state wins)\n // Note: we verify the event fired correctly\n expect(onEventChange).toHaveBeenCalledTimes(1);\n expect(capturedLayouts[0]).toMatchObject(nextInternalLayout);\n });\n\n it('Case 3: alternates between props.layout updates and on(change) events', () => {\n const config1 = createConfig(0);\n const config2 = createConfig(2);\n const internalChange: Config['layout'] = [{i: 'item1', x: 5, y: 0, w: 2, h: 2}];\n const config3 = createConfig(8);\n\n const dashkit = new DashKit(\n DashKit.defaultProps as unknown as ConstructorParameters<typeof DashKit>[0],\n );\n const onChange = jest.fn();\n const onEventChange = jest.fn();\n\n dashkit.on('change', onEventChange);\n\n const {rerender} = render(\n <TestComponent\n {...createProps({\n config: config1,\n emitDashKitEvent: dashkit[_emitSymbol],\n onChange,\n })}\n />,\n );\n\n // Start with config1\n expect(captured.layout).toMatchObject(config1.layout);\n\n // Update via props (external update)\n act(() => {\n rerender(\n <TestComponent\n {...createProps({\n config: config2,\n emitDashKitEvent: dashkit[_emitSymbol],\n onChange,\n })}\n />,\n );\n });\n expect(captured.layout).toMatchObject(config2.layout);\n\n // Update via internal change (user drag)\n act(() => {\n captured.layoutChange(internalChange);\n });\n expect(onEventChange).toHaveBeenCalledTimes(1);\n expect(onEventChange.mock.calls[0][0].layout).toEqual(internalChange);\n\n // Update via props again (external update wins)\n act(() => {\n rerender(\n <TestComponent\n {...createProps({\n config: config3,\n emitDashKitEvent: dashkit[_emitSymbol],\n onChange,\n })}\n />,\n );\n });\n expect(captured.layout).toMatchObject(config3.layout);\n });\n});\n"]}
@@ -0,0 +1,225 @@
1
+ jest.mock('@gravity-ui/icons', () => ({
2
+ Ellipsis: 'ellipsis',
3
+ Gear: 'gear',
4
+ Xmark: 'xmark',
5
+ }));
6
+ jest.mock('@gravity-ui/uikit', () => {
7
+ const ReactMock = require('react');
8
+ return {
9
+ Button: ({ children }) => ReactMock.createElement('button', null, children),
10
+ DropdownMenu: () => null,
11
+ Icon: () => null,
12
+ };
13
+ });
14
+ jest.mock('../../GridLayout/GridLayout', () => {
15
+ const ReactMock = require('react');
16
+ return ReactMock.forwardRef(() => null);
17
+ });
18
+ import { emitDashKitChangeEvent } from "../../../utils/index.js";
19
+ import { DashKit, _emitSymbol } from "../index.js";
20
+ const TEST_CONFIG = {
21
+ salt: 'test',
22
+ counter: 1,
23
+ items: [{ id: 'item1', type: 'test-event', namespace: 'default', data: {} }],
24
+ layout: [{ i: 'item1', x: 0, y: 0, w: 2, h: 2 }],
25
+ aliases: {},
26
+ connections: [],
27
+ };
28
+ const NEXT_LAYOUT = [{ i: 'item1', x: 1, y: 0, w: 2, h: 2 }];
29
+ const NEXT_CONFIG = Object.assign(Object.assign({}, TEST_CONFIG), { layout: NEXT_LAYOUT });
30
+ describe('DashKit change event — onChange gate', () => {
31
+ it('no handler: onChange fires with full config when layout changes', () => {
32
+ const onChange = jest.fn();
33
+ const emitDashKitEvent = jest.fn();
34
+ emitDashKitChangeEvent({
35
+ config: TEST_CONFIG,
36
+ newConfig: NEXT_CONFIG,
37
+ onChange,
38
+ emitDashKitEvent,
39
+ });
40
+ expect(emitDashKitEvent).toHaveBeenCalledTimes(1);
41
+ expect(onChange).toHaveBeenCalledTimes(1);
42
+ expect(onChange.mock.calls[0][0].config.layout).toEqual(NEXT_LAYOUT);
43
+ });
44
+ it('handler without preventDefault: onChange fires and handler receives correct event', () => {
45
+ const instance = new DashKit(DashKit.defaultProps);
46
+ const onChange = jest.fn();
47
+ const handler = jest.fn();
48
+ instance.on('change', handler);
49
+ emitDashKitChangeEvent({
50
+ config: TEST_CONFIG,
51
+ newConfig: NEXT_CONFIG,
52
+ onChange,
53
+ emitDashKitEvent: instance[_emitSymbol],
54
+ });
55
+ expect(handler).toHaveBeenCalledTimes(1);
56
+ expect(onChange).toHaveBeenCalledTimes(1);
57
+ const event = handler.mock.calls[0][0];
58
+ expect(event.layout).toEqual(NEXT_LAYOUT);
59
+ expect(event.previousLayout).toEqual(TEST_CONFIG.layout);
60
+ expect(event.defaultPrevented).toBe(false);
61
+ });
62
+ it('handler calls preventDefault: onChange does NOT fire; event carries correct patches', () => {
63
+ const instance = new DashKit(DashKit.defaultProps);
64
+ const onChange = jest.fn();
65
+ let capturedEvent;
66
+ instance.on('change', (e) => {
67
+ capturedEvent = e;
68
+ e.preventDefault();
69
+ });
70
+ emitDashKitChangeEvent({
71
+ config: TEST_CONFIG,
72
+ newConfig: NEXT_CONFIG,
73
+ onChange,
74
+ emitDashKitEvent: instance[_emitSymbol],
75
+ });
76
+ expect(onChange).not.toHaveBeenCalled();
77
+ expect(capturedEvent).toBeDefined();
78
+ if (capturedEvent) {
79
+ expect(capturedEvent.patches).toEqual([{ i: 'item1', x: 1, y: 0, w: 2, h: 2 }]);
80
+ expect(capturedEvent.layout).toEqual(NEXT_LAYOUT);
81
+ expect(capturedEvent.previousLayout).toEqual(TEST_CONFIG.layout);
82
+ expect(capturedEvent.defaultPrevented).toBe(true);
83
+ }
84
+ });
85
+ it('same layout: event and onChange do not fire', () => {
86
+ const onChange = jest.fn();
87
+ const emitDashKitEvent = jest.fn();
88
+ emitDashKitChangeEvent({
89
+ config: TEST_CONFIG,
90
+ newConfig: TEST_CONFIG,
91
+ onChange,
92
+ emitDashKitEvent,
93
+ });
94
+ expect(emitDashKitEvent).not.toHaveBeenCalled();
95
+ expect(onChange).not.toHaveBeenCalled();
96
+ });
97
+ });
98
+ describe('DashKit event emitter', () => {
99
+ it('unsubscribe stops handler from being called', () => {
100
+ const instance = new DashKit(DashKit.defaultProps);
101
+ const handler = jest.fn();
102
+ const unsubscribe = instance.on('change', handler);
103
+ unsubscribe();
104
+ const event = {
105
+ patches: [],
106
+ layout: [],
107
+ previousLayout: [],
108
+ preventDefault: jest.fn(),
109
+ defaultPrevented: false,
110
+ };
111
+ instance[_emitSymbol]('change', event);
112
+ expect(handler).not.toHaveBeenCalled();
113
+ });
114
+ it('handler error does not stop other handlers; onChange still fires', () => {
115
+ const instance = new DashKit(DashKit.defaultProps);
116
+ const onChange = jest.fn();
117
+ const normalHandler = jest.fn();
118
+ instance.on('change', () => {
119
+ throw new Error('boom');
120
+ });
121
+ instance.on('change', normalHandler);
122
+ const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
123
+ emitDashKitChangeEvent({
124
+ config: TEST_CONFIG,
125
+ newConfig: NEXT_CONFIG,
126
+ onChange,
127
+ emitDashKitEvent: instance[_emitSymbol],
128
+ });
129
+ consoleErrorSpy.mockRestore();
130
+ expect(normalHandler).toHaveBeenCalledTimes(1);
131
+ expect(onChange).toHaveBeenCalledTimes(1);
132
+ });
133
+ });
134
+ describe('DashKit change event — internal baseline (preventDefault flow)', () => {
135
+ it('preventDefault flow: sequential changes produce incremental patches when baseline is updated', () => {
136
+ const instance = new DashKit(DashKit.defaultProps);
137
+ const onChange = jest.fn();
138
+ const patches = [];
139
+ instance.on('change', (e) => {
140
+ patches.push([...e.patches]);
141
+ e.preventDefault();
142
+ });
143
+ const config1 = Object.assign(Object.assign({}, TEST_CONFIG), { items: [
144
+ { id: 'item1', type: 'test-event', namespace: 'default', data: {} },
145
+ { id: 'item2', type: 'test-event', namespace: 'default', data: {} },
146
+ ], layout: [
147
+ { i: 'item1', x: 0, y: 0, w: 2, h: 2 },
148
+ { i: 'item2', x: 2, y: 0, w: 2, h: 2 },
149
+ ] });
150
+ const config2 = Object.assign(Object.assign({}, config1), { layout: [
151
+ { i: 'item1', x: 1, y: 0, w: 2, h: 2 }, // item1 moved to x=1
152
+ { i: 'item2', x: 2, y: 0, w: 2, h: 2 }, // item2 unchanged
153
+ ] });
154
+ const config3 = Object.assign(Object.assign({}, config1), { layout: [
155
+ { i: 'item1', x: 1, y: 0, w: 2, h: 2 }, // item1 unchanged
156
+ { i: 'item2', x: 3, y: 0, w: 2, h: 2 }, // item2 moved to x=3
157
+ ] });
158
+ // First change: item1 moved from x=0 to x=1
159
+ emitDashKitChangeEvent({
160
+ config: config1,
161
+ newConfig: config2,
162
+ onChange,
163
+ emitDashKitEvent: instance[_emitSymbol],
164
+ });
165
+ // Second change: In withContext, the internal baseline is updated automatically
166
+ // after each change. Here we simulate this behavior by passing config2 as baseline.
167
+ emitDashKitChangeEvent({
168
+ config: config2, // internal baseline updated to config2.layout
169
+ newConfig: config3,
170
+ onChange,
171
+ emitDashKitEvent: instance[_emitSymbol],
172
+ });
173
+ expect(onChange).not.toHaveBeenCalled(); // preventDefault works
174
+ expect(patches).toHaveLength(2);
175
+ // First patch: only item1
176
+ expect(patches[0]).toEqual([{ i: 'item1', x: 1, y: 0, w: 2, h: 2 }]);
177
+ // Second patch: only item2 (NO accumulation!)
178
+ // This works because baseline was updated from config1.layout to config2.layout
179
+ expect(patches[1]).toEqual([{ i: 'item2', x: 3, y: 0, w: 2, h: 2 }]);
180
+ });
181
+ it('legacy flow: onChange updates props and patches work correctly', () => {
182
+ const instance = new DashKit(DashKit.defaultProps);
183
+ let currentConfig = Object.assign(Object.assign({}, TEST_CONFIG), { items: [
184
+ { id: 'item1', type: 'test-event', namespace: 'default', data: {} },
185
+ { id: 'item2', type: 'test-event', namespace: 'default', data: {} },
186
+ ], layout: [
187
+ { i: 'item1', x: 0, y: 0, w: 2, h: 2 },
188
+ { i: 'item2', x: 2, y: 0, w: 2, h: 2 },
189
+ ] });
190
+ const patches = [];
191
+ instance.on('change', (e) => {
192
+ patches.push([...e.patches]);
193
+ // NO preventDefault → onChange will fire
194
+ });
195
+ const onChange = jest.fn(({ config }) => {
196
+ currentConfig = config; // consumer updates state
197
+ });
198
+ const config2 = Object.assign(Object.assign({}, currentConfig), { layout: [
199
+ { i: 'item1', x: 1, y: 0, w: 2, h: 2 }, // item1 moved to x=1
200
+ { i: 'item2', x: 2, y: 0, w: 2, h: 2 }, // item2 unchanged
201
+ ] });
202
+ emitDashKitChangeEvent({
203
+ config: currentConfig,
204
+ newConfig: config2,
205
+ onChange,
206
+ emitDashKitEvent: instance[_emitSymbol],
207
+ });
208
+ expect(onChange).toHaveBeenCalledTimes(1);
209
+ expect(patches[0]).toEqual([{ i: 'item1', x: 1, y: 0, w: 2, h: 2 }]);
210
+ // Second change with updated config
211
+ const config3 = Object.assign(Object.assign({}, currentConfig), { layout: [
212
+ { i: 'item1', x: 1, y: 0, w: 2, h: 2 }, // item1 unchanged
213
+ { i: 'item2', x: 3, y: 0, w: 2, h: 2 }, // item2 moved to x=3
214
+ ] });
215
+ emitDashKitChangeEvent({
216
+ config: currentConfig, // ← updated after first onChange
217
+ newConfig: config3,
218
+ onChange,
219
+ emitDashKitEvent: instance[_emitSymbol],
220
+ });
221
+ expect(onChange).toHaveBeenCalledTimes(2);
222
+ expect(patches[1]).toEqual([{ i: 'item2', x: 3, y: 0, w: 2, h: 2 }]); // only new changes
223
+ });
224
+ });
225
+ //# sourceMappingURL=events.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.test.js","sourceRoot":"../../../../../src","sources":["components/DashKit/__tests__/events.test.tsx"],"names":[],"mappings":"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","sourcesContent":["jest.mock('@gravity-ui/icons', () => ({\n Ellipsis: 'ellipsis',\n Gear: 'gear',\n Xmark: 'xmark',\n}));\n\njest.mock('@gravity-ui/uikit', () => {\n const ReactMock = require('react');\n\n return {\n Button: ({children}: {children?: unknown}) =>\n ReactMock.createElement('button', null, children),\n DropdownMenu: () => null,\n Icon: () => null,\n };\n});\n\njest.mock('../../GridLayout/GridLayout', () => {\n const ReactMock = require('react');\n\n return ReactMock.forwardRef(() => null);\n});\n\nimport type {Config} from '../../../shared';\nimport type {DashKitEventMap, DashKitLayoutPatch} from '../../../typings';\nimport {emitDashKitChangeEvent} from '../../../utils';\nimport {DashKit, _emitSymbol} from '../../DashKit';\n\nconst TEST_CONFIG: Config = {\n salt: 'test',\n counter: 1,\n items: [{id: 'item1', type: 'test-event', namespace: 'default', data: {}}],\n layout: [{i: 'item1', x: 0, y: 0, w: 2, h: 2}],\n aliases: {},\n connections: [],\n};\n\nconst NEXT_LAYOUT: Config['layout'] = [{i: 'item1', x: 1, y: 0, w: 2, h: 2}];\nconst NEXT_CONFIG: Config = {...TEST_CONFIG, layout: NEXT_LAYOUT};\n\ndescribe('DashKit change event — onChange gate', () => {\n it('no handler: onChange fires with full config when layout changes', () => {\n const onChange = jest.fn();\n const emitDashKitEvent = jest.fn();\n\n emitDashKitChangeEvent({\n config: TEST_CONFIG,\n newConfig: NEXT_CONFIG,\n onChange,\n emitDashKitEvent,\n });\n\n expect(emitDashKitEvent).toHaveBeenCalledTimes(1);\n expect(onChange).toHaveBeenCalledTimes(1);\n expect(onChange.mock.calls[0][0].config.layout).toEqual(NEXT_LAYOUT);\n });\n\n it('handler without preventDefault: onChange fires and handler receives correct event', () => {\n const instance = new DashKit(DashKit.defaultProps as never);\n const onChange = jest.fn();\n const handler = jest.fn();\n\n instance.on('change', handler);\n\n emitDashKitChangeEvent({\n config: TEST_CONFIG,\n newConfig: NEXT_CONFIG,\n onChange,\n emitDashKitEvent: instance[_emitSymbol],\n });\n\n expect(handler).toHaveBeenCalledTimes(1);\n expect(onChange).toHaveBeenCalledTimes(1);\n const event = handler.mock.calls[0][0] as DashKitEventMap['change'];\n expect(event.layout).toEqual(NEXT_LAYOUT);\n expect(event.previousLayout).toEqual(TEST_CONFIG.layout);\n expect(event.defaultPrevented).toBe(false);\n });\n\n it('handler calls preventDefault: onChange does NOT fire; event carries correct patches', () => {\n const instance = new DashKit(DashKit.defaultProps as never);\n const onChange = jest.fn();\n let capturedEvent: DashKitEventMap['change'] | undefined;\n\n instance.on('change', (e) => {\n capturedEvent = e;\n e.preventDefault();\n });\n\n emitDashKitChangeEvent({\n config: TEST_CONFIG,\n newConfig: NEXT_CONFIG,\n onChange,\n emitDashKitEvent: instance[_emitSymbol],\n });\n\n expect(onChange).not.toHaveBeenCalled();\n expect(capturedEvent).toBeDefined();\n if (capturedEvent) {\n expect(capturedEvent.patches).toEqual([{i: 'item1', x: 1, y: 0, w: 2, h: 2}]);\n expect(capturedEvent.layout).toEqual(NEXT_LAYOUT);\n expect(capturedEvent.previousLayout).toEqual(TEST_CONFIG.layout);\n expect(capturedEvent.defaultPrevented).toBe(true);\n }\n });\n\n it('same layout: event and onChange do not fire', () => {\n const onChange = jest.fn();\n const emitDashKitEvent = jest.fn();\n\n emitDashKitChangeEvent({\n config: TEST_CONFIG,\n newConfig: TEST_CONFIG,\n onChange,\n emitDashKitEvent,\n });\n\n expect(emitDashKitEvent).not.toHaveBeenCalled();\n expect(onChange).not.toHaveBeenCalled();\n });\n});\n\ndescribe('DashKit event emitter', () => {\n it('unsubscribe stops handler from being called', () => {\n const instance = new DashKit(DashKit.defaultProps as never);\n const handler = jest.fn();\n\n const unsubscribe = instance.on('change', handler);\n unsubscribe();\n\n const event = {\n patches: [],\n layout: [],\n previousLayout: [],\n preventDefault: jest.fn(),\n defaultPrevented: false,\n } as unknown as DashKitEventMap['change'];\n\n instance[_emitSymbol]('change', event);\n\n expect(handler).not.toHaveBeenCalled();\n });\n\n it('handler error does not stop other handlers; onChange still fires', () => {\n const instance = new DashKit(DashKit.defaultProps as never);\n const onChange = jest.fn();\n const normalHandler = jest.fn();\n\n instance.on('change', () => {\n throw new Error('boom');\n });\n instance.on('change', normalHandler);\n\n const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});\n\n emitDashKitChangeEvent({\n config: TEST_CONFIG,\n newConfig: NEXT_CONFIG,\n onChange,\n emitDashKitEvent: instance[_emitSymbol],\n });\n\n consoleErrorSpy.mockRestore();\n\n expect(normalHandler).toHaveBeenCalledTimes(1);\n expect(onChange).toHaveBeenCalledTimes(1);\n });\n});\n\ndescribe('DashKit change event — internal baseline (preventDefault flow)', () => {\n it('preventDefault flow: sequential changes produce incremental patches when baseline is updated', () => {\n const instance = new DashKit(DashKit.defaultProps as never);\n const onChange = jest.fn();\n const patches: Array<DashKitLayoutPatch[]> = [];\n\n instance.on('change', (e) => {\n patches.push([...e.patches]);\n e.preventDefault();\n });\n\n const config1: Config = {\n ...TEST_CONFIG,\n items: [\n {id: 'item1', type: 'test-event', namespace: 'default', data: {}},\n {id: 'item2', type: 'test-event', namespace: 'default', data: {}},\n ],\n layout: [\n {i: 'item1', x: 0, y: 0, w: 2, h: 2},\n {i: 'item2', x: 2, y: 0, w: 2, h: 2},\n ],\n };\n\n const config2: Config = {\n ...config1,\n layout: [\n {i: 'item1', x: 1, y: 0, w: 2, h: 2}, // item1 moved to x=1\n {i: 'item2', x: 2, y: 0, w: 2, h: 2}, // item2 unchanged\n ],\n };\n\n const config3: Config = {\n ...config1,\n layout: [\n {i: 'item1', x: 1, y: 0, w: 2, h: 2}, // item1 unchanged\n {i: 'item2', x: 3, y: 0, w: 2, h: 2}, // item2 moved to x=3\n ],\n };\n\n // First change: item1 moved from x=0 to x=1\n emitDashKitChangeEvent({\n config: config1,\n newConfig: config2,\n onChange,\n emitDashKitEvent: instance[_emitSymbol],\n });\n\n // Second change: In withContext, the internal baseline is updated automatically\n // after each change. Here we simulate this behavior by passing config2 as baseline.\n emitDashKitChangeEvent({\n config: config2, // internal baseline updated to config2.layout\n newConfig: config3,\n onChange,\n emitDashKitEvent: instance[_emitSymbol],\n });\n\n expect(onChange).not.toHaveBeenCalled(); // preventDefault works\n expect(patches).toHaveLength(2);\n\n // First patch: only item1\n expect(patches[0]).toEqual([{i: 'item1', x: 1, y: 0, w: 2, h: 2}]);\n\n // Second patch: only item2 (NO accumulation!)\n // This works because baseline was updated from config1.layout to config2.layout\n expect(patches[1]).toEqual([{i: 'item2', x: 3, y: 0, w: 2, h: 2}]);\n });\n\n it('legacy flow: onChange updates props and patches work correctly', () => {\n const instance = new DashKit(DashKit.defaultProps as never);\n let currentConfig: Config = {\n ...TEST_CONFIG,\n items: [\n {id: 'item1', type: 'test-event', namespace: 'default', data: {}},\n {id: 'item2', type: 'test-event', namespace: 'default', data: {}},\n ],\n layout: [\n {i: 'item1', x: 0, y: 0, w: 2, h: 2},\n {i: 'item2', x: 2, y: 0, w: 2, h: 2},\n ],\n };\n const patches: Array<Array<{i: string; x?: number; y?: number; w?: number; h?: number}>> =\n [];\n\n instance.on('change', (e) => {\n patches.push([...e.patches]);\n // NO preventDefault → onChange will fire\n });\n\n const onChange = jest.fn(({config}) => {\n currentConfig = config; // consumer updates state\n });\n\n const config2 = {\n ...currentConfig,\n layout: [\n {i: 'item1', x: 1, y: 0, w: 2, h: 2}, // item1 moved to x=1\n {i: 'item2', x: 2, y: 0, w: 2, h: 2}, // item2 unchanged\n ],\n };\n\n emitDashKitChangeEvent({\n config: currentConfig,\n newConfig: config2,\n onChange,\n emitDashKitEvent: instance[_emitSymbol],\n });\n\n expect(onChange).toHaveBeenCalledTimes(1);\n expect(patches[0]).toEqual([{i: 'item1', x: 1, y: 0, w: 2, h: 2}]);\n\n // Second change with updated config\n const config3 = {\n ...currentConfig,\n layout: [\n {i: 'item1', x: 1, y: 0, w: 2, h: 2}, // item1 unchanged\n {i: 'item2', x: 3, y: 0, w: 2, h: 2}, // item2 moved to x=3\n ],\n };\n\n emitDashKitChangeEvent({\n config: currentConfig, // ← updated after first onChange\n newConfig: config3,\n onChange,\n emitDashKitEvent: instance[_emitSymbol],\n });\n\n expect(onChange).toHaveBeenCalledTimes(2);\n expect(patches[1]).toEqual([{i: 'item2', x: 3, y: 0, w: 2, h: 2}]); // only new changes\n });\n});\n"]}
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import type { DashKitWithContextProps } from "../../hocs/withContext.js";
3
3
  import "./DashKitView.css";
4
- type DashKitViewProps = Omit<DashKitWithContextProps, 'layout' | 'forwardedMetaRef'>;
4
+ type DashKitViewProps = Omit<DashKitWithContextProps, 'forwardedMetaRef'>;
5
5
  declare const DashKitViewForwardedMeta: React.ForwardRefExoticComponent<DashKitViewProps & React.RefAttributes<unknown>>;
6
6
  export default DashKitViewForwardedMeta;
@@ -1,7 +1,6 @@
1
1
  import React from 'react';
2
2
  import { DashKitContext } from "../../context/index.js";
3
3
  import { withContext } from "../../hocs/withContext.js";
4
- import { useCalcPropsLayout } from "../../hooks/useCalcLayout.js";
5
4
  import { cn } from "../../utils/cn.js";
6
5
  import GridLayout from "../GridLayout/GridLayout.js";
7
6
  import MobileLayout from "../MobileLayout/MobileLayout.js";
@@ -14,8 +13,7 @@ function DashKitView() {
14
13
  }
15
14
  const DashKitViewWithContext = withContext(DashKitView);
16
15
  const DashKitViewForwardedMeta = React.forwardRef((props, ref) => {
17
- const layout = useCalcPropsLayout(props.config, props.registerManager);
18
- return React.createElement(DashKitViewWithContext, Object.assign({}, props, { layout: layout, forwardedMetaRef: ref }));
16
+ return React.createElement(DashKitViewWithContext, Object.assign({}, props, { forwardedMetaRef: ref }));
19
17
  });
20
18
  DashKitViewForwardedMeta.displayName = 'DashKitViewForwardedMeta';
21
19
  export default DashKitViewForwardedMeta;
@@ -1 +1 @@
1
- {"version":3,"file":"DashKitView.js","sourceRoot":"../../../../src","sources":["components/DashKitView/DashKitView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAC,cAAc,EAAC,+BAAsB;AAC7C,OAAO,EAAC,WAAW,EAAC,kCAA+B;AAEnD,OAAO,EAAC,kBAAkB,EAAC,qCAAkC;AAC7D,OAAO,EAAC,EAAE,EAAC,0BAAuB;AAClC,OAAO,UAAU,oCAAiC;AAClD,OAAO,YAAY,wCAAqC;AAExD,2BAA4B;AAE5B,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;AAIxB,SAAS,WAAW;IAChB,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;IACjD,MAAM,EAAC,eAAe,EAAE,gBAAgB,EAAC,GAAG,OAAO,CAAC;IAEpD,OAAO,CACH,6BAAK,SAAS,EAAE,CAAC,CAAC,EAAC,KAAK,EAAE,eAAe,CAAC,QAAQ,CAAC,KAAK,EAAC,CAAC,IACrD,eAAe,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CACjC,oBAAC,YAAY,OAAG,CACnB,CAAC,CAAC,CAAC,CACA,oBAAC,UAAU,IAAC,GAAG,EAAE,gBAAgB,GAAI,CACxC,CACC,CACT,CAAC;AACN,CAAC;AAED,MAAM,sBAAsB,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;AAExD,MAAM,wBAAwB,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,KAAuB,EAAE,GAAG,EAAE,EAAE;IAC/E,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IAEvE,OAAO,oBAAC,sBAAsB,oBAAK,KAAK,IAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAAC;AACxF,CAAC,CAAC,CAAC;AAEH,wBAAwB,CAAC,WAAW,GAAG,0BAA0B,CAAC;AAElE,eAAe,wBAAwB,CAAC","sourcesContent":["import React from 'react';\n\nimport {DashKitContext} from '../../context';\nimport {withContext} from '../../hocs/withContext';\nimport type {DashKitWithContextProps} from '../../hocs/withContext';\nimport {useCalcPropsLayout} from '../../hooks/useCalcLayout';\nimport {cn} from '../../utils/cn';\nimport GridLayout from '../GridLayout/GridLayout';\nimport MobileLayout from '../MobileLayout/MobileLayout';\n\nimport './DashKitView.scss';\n\nconst b = cn('dashkit');\n\ntype DashKitViewProps = Omit<DashKitWithContextProps, 'layout' | 'forwardedMetaRef'>;\n\nfunction DashKitView() {\n const context = React.useContext(DashKitContext);\n const {registerManager, forwardedMetaRef} = context;\n\n return (\n <div className={b({theme: registerManager.settings.theme})}>\n {registerManager.settings.isMobile ? (\n <MobileLayout />\n ) : (\n <GridLayout ref={forwardedMetaRef} />\n )}\n </div>\n );\n}\n\nconst DashKitViewWithContext = withContext(DashKitView);\n\nconst DashKitViewForwardedMeta = React.forwardRef((props: DashKitViewProps, ref) => {\n const layout = useCalcPropsLayout(props.config, props.registerManager);\n\n return <DashKitViewWithContext {...props} layout={layout} forwardedMetaRef={ref} />;\n});\n\nDashKitViewForwardedMeta.displayName = 'DashKitViewForwardedMeta';\n\nexport default DashKitViewForwardedMeta;\n"]}
1
+ {"version":3,"file":"DashKitView.js","sourceRoot":"../../../../src","sources":["components/DashKitView/DashKitView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAC,cAAc,EAAC,+BAAsB;AAC7C,OAAO,EAAC,WAAW,EAAC,kCAA+B;AAEnD,OAAO,EAAC,EAAE,EAAC,0BAAuB;AAClC,OAAO,UAAU,oCAAiC;AAClD,OAAO,YAAY,wCAAqC;AAExD,2BAA4B;AAE5B,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;AAIxB,SAAS,WAAW;IAChB,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;IACjD,MAAM,EAAC,eAAe,EAAE,gBAAgB,EAAC,GAAG,OAAO,CAAC;IAEpD,OAAO,CACH,6BAAK,SAAS,EAAE,CAAC,CAAC,EAAC,KAAK,EAAE,eAAe,CAAC,QAAQ,CAAC,KAAK,EAAC,CAAC,IACrD,eAAe,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CACjC,oBAAC,YAAY,OAAG,CACnB,CAAC,CAAC,CAAC,CACA,oBAAC,UAAU,IAAC,GAAG,EAAE,gBAAgB,GAAI,CACxC,CACC,CACT,CAAC;AACN,CAAC;AAED,MAAM,sBAAsB,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;AAExD,MAAM,wBAAwB,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,KAAuB,EAAE,GAAG,EAAE,EAAE;IAC/E,OAAO,oBAAC,sBAAsB,oBAAK,KAAK,IAAE,gBAAgB,EAAE,GAAG,IAAI,CAAC;AACxE,CAAC,CAAC,CAAC;AAEH,wBAAwB,CAAC,WAAW,GAAG,0BAA0B,CAAC;AAElE,eAAe,wBAAwB,CAAC","sourcesContent":["import React from 'react';\n\nimport {DashKitContext} from '../../context';\nimport {withContext} from '../../hocs/withContext';\nimport type {DashKitWithContextProps} from '../../hocs/withContext';\nimport {cn} from '../../utils/cn';\nimport GridLayout from '../GridLayout/GridLayout';\nimport MobileLayout from '../MobileLayout/MobileLayout';\n\nimport './DashKitView.scss';\n\nconst b = cn('dashkit');\n\ntype DashKitViewProps = Omit<DashKitWithContextProps, 'forwardedMetaRef'>;\n\nfunction DashKitView() {\n const context = React.useContext(DashKitContext);\n const {registerManager, forwardedMetaRef} = context;\n\n return (\n <div className={b({theme: registerManager.settings.theme})}>\n {registerManager.settings.isMobile ? (\n <MobileLayout />\n ) : (\n <GridLayout ref={forwardedMetaRef} />\n )}\n </div>\n );\n}\n\nconst DashKitViewWithContext = withContext(DashKitView);\n\nconst DashKitViewForwardedMeta = React.forwardRef((props: DashKitViewProps, ref) => {\n return <DashKitViewWithContext {...props} forwardedMetaRef={ref} />;\n});\n\nDashKitViewForwardedMeta.displayName = 'DashKitViewForwardedMeta';\n\nexport default DashKitViewForwardedMeta;\n"]}
@@ -19,7 +19,7 @@ function groupLayoutPropsAreEqual(prev, next) {
19
19
  return !needRerenderIfChanged && !needRerenderGroupScoped && !hasTmpPlaceholderChanged;
20
20
  }
21
21
  export const GroupLayout = React.memo(function GroupLayout({ group, renderItems, offset, properties, compactType, callbacks, layout, temporaryPlaceholder, dragStateRef, sharedDragPositionRef, groupResetRegistryRef, isDragCaptured, isAnyDragging, currentDraggingItemId, isAnyDraggedOut, adjustWidgetLayout, getMemoForwardRefCallback, }) {
22
- const { editMode, noOverlay, focusable, draggableHandleClassName, outerDnDEnable, onItemMountChange, onItemRender, onItemFocus, onItemBlur, } = React.useContext(DashKitContext);
22
+ const { editMode, noOverlay, focusable, draggableHandleClassName, outerDnDEnable, onItemMountChange, onItemRender, onItemFocus, onItemBlur, externalLayoutRevision, } = React.useContext(DashKitContext);
23
23
  // Use group-specific noOverlay if it was explicitly set via groupGridProperties,
24
24
  // otherwise fallback to the dashboard-level noOverlay from context.
25
25
  const resolvedNoOverlay = 'noOverlay' in properties ? properties.noOverlay : noOverlay;
@@ -50,7 +50,7 @@ export const GroupLayout = React.memo(function GroupLayout({ group, renderItems,
50
50
  getMemoForwardRefCallback,
51
51
  editMode,
52
52
  ]);
53
- return (React.createElement(Layout, Object.assign({ key: `group_${group}`, isDraggable: editMode, isResizable: editMode }, properties, { compactType: compactType, layout: layout, draggableCancel: `.${DRAGGABLE_CANCEL_CLASS_NAME}`, draggableHandle: draggableHandleClassName ? `.${draggableHandleClassName}` : undefined, onDragStart: callbacks.onDragStart, onDrag: callbacks.onDrag, onDragStop: callbacks.onDragStop, onResizeStart: callbacks.onResizeStart, onResize: callbacks.onResize, onResizeStop: callbacks.onResizeStop, onDragTargetRestore: callbacks.onDragTargetRestore, onDropDragOver: callbacks.onDropDragOver, onDrop: callbacks.onDrop, dragStateRef: dragStateRef, sharedDragPositionRef: sharedDragPositionRef, groupResetRegistryRef: groupResetRegistryRef, group: group, isDragCaptured: isDragCaptured, isDroppable: Boolean(outerDnDEnable) && editMode }),
53
+ return (React.createElement(Layout, Object.assign({ key: `group_${group}`, isDraggable: editMode, externalLayoutRevision: externalLayoutRevision, isResizable: editMode }, properties, { compactType: compactType, layout: layout, draggableCancel: `.${DRAGGABLE_CANCEL_CLASS_NAME}`, draggableHandle: draggableHandleClassName ? `.${draggableHandleClassName}` : undefined, onDragStart: callbacks.onDragStart, onDrag: callbacks.onDrag, onDragStop: callbacks.onDragStop, onResizeStart: callbacks.onResizeStart, onResize: callbacks.onResize, onResizeStop: callbacks.onResizeStop, onDragTargetRestore: callbacks.onDragTargetRestore, onDropDragOver: callbacks.onDropDragOver, onDrop: callbacks.onDrop, dragStateRef: dragStateRef, sharedDragPositionRef: sharedDragPositionRef, groupResetRegistryRef: groupResetRegistryRef, group: group, isDragCaptured: isDragCaptured, isDroppable: Boolean(outerDnDEnable) && editMode }),
54
54
  itemElements,
55
55
  temporaryPlaceholder));
56
56
  }, groupLayoutPropsAreEqual);