Skip to content

Commit 4a509d5

Browse files
authored
Merge pull request #3411 from adumesny/master
(vue) drag-in widget id fix
2 parents d54efbe + 4c89730 commit 4a509d5

3 files changed

Lines changed: 42 additions & 1 deletion

File tree

‎doc/CHANGES.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ Change log
159159
[title_drag.html](https://gridstackjs.com/demo/title_drag.html).
160160
* fix: [#3188](https://github.com/gridstack/gridstack.js/issues/3188) release the global touch latch when a widget is destroyed mid-touch
161161
* fix: [#2953](https://github.com/gridstack/gridstack.js/issues/2953) API update() honors maxRow like dragging does
162-
* fix: [#2976](https://github.com/gridstack/gridstack.js/issues/2976) (react): render a drag-in widget that has a component but no id
162+
* fix: [#2976](https://github.com/gridstack/gridstack.js/issues/2976) (react, vue): render a drag-in widget that has a component but no id
163163
* fix: [#2703](https://github.com/gridstack/gridstack.js/issues/2703) allow dragging from elements nested inside a button/input handle
164164

165165
## 13.3.0 (2026-09-11)

‎vue/projects/lib/gridstack-vue.test.ts‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,38 @@ describe('GridStack Vue wrapper', () => {
8282
expect(document.querySelector('[data-testid="portal"]')?.textContent).toBe('hello')
8383
})
8484

85+
it('renders a drag-in widget that has a component but no id (#2976)', async () => {
86+
const T = defineComponent({
87+
props: { label: { type: String, default: '' } },
88+
setup(props) {
89+
return () => h('span', { 'data-testid': 'portal' }, props.label)
90+
},
91+
})
92+
93+
const Root = defineComponent({
94+
setup() {
95+
const options = {
96+
column: 12, cellHeight: 50, margin: 0,
97+
children: [],
98+
}
99+
return () =>
100+
h(GridStack, { options, components: { T } })
101+
},
102+
})
103+
104+
;({ app, container } = mountApp(Root))
105+
await flush()
106+
107+
const gridEl = container.querySelector('.grid-stack') as GridHTMLElement
108+
const grid = gridEl.gridstack!
109+
110+
// drag-in nodes don't have ids, only the spec they carry.
111+
grid.addWidget({ x: 0, y: 0, w: 2, h: 2, component: 'T', props: { label: 'hello' } })
112+
await flush()
113+
114+
expect(document.querySelector('[data-testid="portal"]')?.textContent).toBe('hello')
115+
})
116+
85117
it('save() merges useWidgetSerializer into widget props', async () => {
86118
const Num = defineComponent({
87119
props: { start: { type: Number, default: 0 } },

‎vue/projects/lib/src/registry.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import type {
1010
GridStackWidget,
1111
} from './types'
1212

13+
/** @internal minted ids for widgets that arrive without one (sidebar drag-in) - see gsCreateVueComponents */
14+
let syntheticIdSeq = 0
15+
1316
export function installGridStackVueCallbacks(): void {
1417
if (!GridStack.addRemoveCB) {
1518
GridStack.addRemoveCB = gsCreateVueComponents
@@ -60,6 +63,12 @@ export function gsCreateVueComponents(
6063
const el = Utils.createDiv(itemClasses) as GridItemHTMLElement
6164
Utils.createDiv(['grid-stack-item-content'], el)
6265

66+
// Widgets dropped in from a sidebar (GridStack.setupDragIn) carry no id - the same spec is
67+
// dropped over and over, so it can't have one - yet the portal that renders `component` is
68+
// keyed by id. Without one we used to silently skip rendering and leave an empty item (#2976).
69+
// `w` is the object GS turns into the node, so writing it back sticks for save()/updateCB too.
70+
if (!w.id && w.component) w.id = `gs-vue-${++syntheticIdSeq}`
71+
6372
const id = w.id
6473
if (id) {
6574
el._gridItemRef = { id, gridComp: gridHost }

0 commit comments

Comments
 (0)