Skip to content

Commit d16f691

Browse files
feat(lib): add modalSize object to the toggleModal message (#447)
* feat(SHAPE-7911): add modalSize object to the toggleModal message * fix: solve prettier issues * feat(SHAPE-7911): add new state properties userPermissions, isSpaceAdmin and isAIEnabled * fix: solve prettier issues * feat(shape-7911): add new state as context in the demo, replace any by permissions types, add ModalSize type and ModalSize test * test(shape-7911): add undefined in case of null in storyfront, so the null tests are not needed * feat(shape-7911): add request user context action, demo example and sandbox functionality * fix: prettier issues * test: add extra permissions checks * feat: apply suggestions from code review by Demetrius Co-authored-by: Demetrius Feijóo <[email protected]> * fix: include the type for getUserContextMessage * refactor: remove debounce from the new userContextMessage * feat(shape-7912): add demo and sandbox modal sizing functionality * fix: apply suggestions from code review by Demetrius Co-authored-by: Demetrius Feijóo <[email protected]> * refactor: add text field instead --------- Co-authored-by: Demetrius Feijóo <[email protected]>
1 parent d4be70c commit d16f691

31 files changed

+516
-15
lines changed

packages/cli/templates/react/src/components/FieldPluginExample/ModalToggle.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { FunctionComponent } from 'react'
2+
import type { SetModalOpen } from '@storyblok/field-plugin'
23

34
const ModalToggle: FunctionComponent<{
45
isModalOpen: boolean
5-
setModalOpen: (isModalOpen: boolean) => void
6+
setModalOpen: SetModalOpen<number>
67
}> = ({ isModalOpen, setModalOpen }) => {
78
return (
89
<div>

packages/demo/src/components/ContextRequester.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@ export const ContextRequester: PluginComponent = (props) => {
55
const { data, actions } = props
66
return (
77
<Stack gap={2}>
8-
<Typography variant="subtitle1">Story</Typography>
9-
<Typography textAlign="center">{JSON.stringify(data.story)}</Typography>
8+
<Typography variant="subtitle1">Story: </Typography>
9+
<Typography textAlign="center">
10+
{JSON.stringify(data.story, null, 2)}
11+
</Typography>
12+
<Typography variant="subtitle1">Is AI enabled: </Typography>
13+
<Typography textAlign="center">
14+
{data.isAIEnabled ? 'Yes' : 'No'}
15+
</Typography>
1016
<Button
1117
variant="outlined"
1218
color="secondary"

packages/demo/src/components/ModalToggle.tsx

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,53 @@
1-
import { Button, Stack, Typography } from '@mui/material'
1+
import { useState } from 'react'
2+
import { Button, Stack, Typography, TextField, Grid } from '@mui/material'
23
import { PluginComponent } from './FieldPluginDemo'
4+
import type { ModalSize } from '@storyblok/field-plugin'
35

46
export const ModalToggle: PluginComponent = (props) => {
57
const { actions, data } = props
8+
const [modalSize, setModalSize] = useState<ModalSize>({
9+
width: '50%',
10+
height: '100%',
11+
})
12+
613
return (
714
<Stack gap={2}>
815
<Typography variant="subtitle1">Modal</Typography>
16+
<Grid
17+
container
18+
spacing={2}
19+
>
20+
<Grid
21+
xs={6}
22+
item
23+
>
24+
<TextField
25+
label="Set modal width:"
26+
value={modalSize.width}
27+
fullWidth
28+
onChange={(e) =>
29+
setModalSize({ ...modalSize, width: e.target.value })
30+
}
31+
/>
32+
</Grid>
33+
<Grid
34+
xs={6}
35+
item
36+
>
37+
<TextField
38+
label="Set modal height:"
39+
value={modalSize.height}
40+
fullWidth
41+
onChange={(e) =>
42+
setModalSize({ ...modalSize, height: e.target.value })
43+
}
44+
/>
45+
</Grid>
46+
</Grid>
947
<Button
1048
variant="outlined"
1149
color="secondary"
12-
onClick={() => actions.setModalOpen(!data.isModalOpen)}
50+
onClick={() => actions.setModalOpen(!data.isModalOpen, modalSize)}
1351
>
1452
Toggle Modal
1553
</Button>

packages/demo/src/components/NonModalView.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ValueMutator } from './ValueMutator'
44
import { HeightChangeDemo } from './HeightChangeDemo'
55
import { AssetSelector } from './AssetSelector'
66
import { ContextRequester } from './ContextRequester'
7+
import { UserContextRequester } from './UserContextRequester'
78
import { PluginComponent } from './FieldPluginDemo'
89
import { LanguageView } from './LanguageView'
910

@@ -12,9 +13,9 @@ export const NonModalView: PluginComponent = (props) => (
1213
<Stack gap={6}>
1314
<ModalToggle {...props} />
1415
<ValueMutator {...props} />
15-
<ModalToggle {...props} />
1616
<AssetSelector {...props} />
1717
<ContextRequester {...props} />
18+
<UserContextRequester {...props} />
1819
<HeightChangeDemo {...props} />
1920
<LanguageView {...props} />
2021
</Stack>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useState } from 'react'
2+
import { Button, Stack, Typography } from '@mui/material'
3+
import { PluginComponent } from './FieldPluginDemo'
4+
import { UserData } from '@storyblok/field-plugin'
5+
6+
export const UserContextRequester: PluginComponent = (props) => {
7+
const { actions } = props
8+
const [user, setUser] = useState<UserData>({
9+
isSpaceAdmin: false,
10+
permissions: undefined,
11+
})
12+
return (
13+
<Stack gap={2}>
14+
<Typography variant="subtitle1">User data: </Typography>
15+
<Typography>Permissions: </Typography>
16+
<Typography textAlign="center">
17+
{JSON.stringify(user.permissions, null, 2)}
18+
</Typography>
19+
<Typography>Is space admin? </Typography>
20+
<Typography textAlign="center">
21+
{user.isSpaceAdmin ? 'Yes' : 'No'}
22+
</Typography>
23+
<Button
24+
variant="outlined"
25+
color="secondary"
26+
onClick={async () => setUser(await actions.requestUserContext())}
27+
>
28+
Request User Context
29+
</Button>
30+
</Stack>
31+
)
32+
}
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1-
import { Asset, StoryData } from '../messaging'
2-
import { FieldPluginData } from './FieldPluginData'
1+
import type { Asset, StoryData, UserData, ModalSize } from '../messaging'
2+
import type { FieldPluginData } from './FieldPluginData'
33

44
export type SetContent<Content> = (
55
content: Content,
66
) => Promise<FieldPluginData<Content>>
77
export type SetModalOpen<Content> = (
88
isModalOpen: boolean,
9+
modalSize?: ModalSize,
910
) => Promise<FieldPluginData<Content>>
1011
export type RequestContext = () => Promise<StoryData>
12+
export type RequestUserContext = () => Promise<UserData>
1113
export type SelectAsset = () => Promise<Asset>
1214
export type Initialize<Content> = () => Promise<FieldPluginData<Content>>
1315

1416
export type FieldPluginActions<Content> = {
1517
setContent: SetContent<Content>
1618
setModalOpen: SetModalOpen<Content>
1719
requestContext: RequestContext
20+
requestUserContext: RequestUserContext
1821
selectAsset: SelectAsset
1922
}

packages/field-plugin/src/createFieldPlugin/FieldPluginData.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export type FieldPluginData<Content> = {
1717
token: string | undefined
1818
uid: string
1919
translatable: boolean
20+
isAIEnabled: boolean
2021
releases: Release[]
2122
releaseId: number | undefined
2223
}

packages/field-plugin/src/createFieldPlugin/createPluginActions/callbackQueue.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
AssetSelectedMessage,
33
ContextRequestMessage,
4+
UserContextRequestMessage,
45
LoadedMessage,
56
OnMessage,
67
StateChangedMessage,
@@ -12,6 +13,7 @@ export type CallbackId = string
1213
type CallbackMap = {
1314
asset: Record<CallbackId, OnMessage<AssetSelectedMessage>>
1415
context: Record<CallbackId, OnMessage<ContextRequestMessage>>
16+
userContext: Record<CallbackId, OnMessage<UserContextRequestMessage>>
1517
stateChanged: Record<CallbackId, OnMessage<StateChangedMessage>>
1618
loaded: Record<CallbackId, OnMessage<LoadedMessage>>
1719
}
@@ -21,6 +23,7 @@ export const callbackQueue = () => {
2123
let callbackMap: CallbackMap = {
2224
asset: {},
2325
context: {},
26+
userContext: {},
2427
stateChanged: {},
2528
loaded: {},
2629
}

packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginActions.test.ts

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { createPluginActions } from './createPluginActions'
2-
import {
2+
import type {
33
AssetModalChangeMessage,
44
GetContextMessage,
5+
GetUserContextMessage,
56
ModalChangeMessage,
67
ValueChangeMessage,
78
} from '../../messaging'
@@ -72,6 +73,7 @@ describe('createPluginActions', () => {
7273
spaceId: null,
7374
userId: undefined,
7475
blockId: undefined,
76+
isAIEnabled: false,
7577
releases: [],
7678
releaseId: undefined,
7779
})
@@ -135,6 +137,44 @@ describe('createPluginActions', () => {
135137
} satisfies Partial<ModalChangeMessage>),
136138
)
137139
})
140+
it('sends modalSize to the container when opening the modal', () => {
141+
const { uid, postToContainer, onUpdateState } = mock()
142+
const {
143+
actions: { setModalOpen },
144+
} = createPluginActions({
145+
uid,
146+
postToContainer,
147+
onUpdateState,
148+
validateContent,
149+
})
150+
151+
setModalOpen(true, { width: '50%' })
152+
expect(postToContainer).toHaveBeenCalledWith(
153+
expect.objectContaining({
154+
event: 'toggleModal',
155+
status: true,
156+
modalSize: { width: '50%' },
157+
} satisfies Partial<ModalChangeMessage>),
158+
)
159+
160+
setModalOpen(true, { height: '50%' })
161+
expect(postToContainer).toHaveBeenCalledWith(
162+
expect.objectContaining({
163+
event: 'toggleModal',
164+
status: true,
165+
modalSize: { height: '50%' },
166+
} satisfies Partial<ModalChangeMessage>),
167+
)
168+
169+
setModalOpen(true, { width: '50%', height: '50%' })
170+
expect(postToContainer).toHaveBeenCalledWith(
171+
expect.objectContaining({
172+
event: 'toggleModal',
173+
status: true,
174+
modalSize: { width: '50%', height: '50%' },
175+
} satisfies Partial<ModalChangeMessage>),
176+
)
177+
})
138178
})
139179
describe('value state change', () => {
140180
it('updates the value state when setContent is called', () => {
@@ -175,6 +215,7 @@ describe('createPluginActions', () => {
175215
)
176216
})
177217
})
218+
178219
describe('requestContext()', () => {
179220
it('send a message to the container to request the story', () => {
180221
const { uid, postToContainer, onUpdateState } = mock()
@@ -194,6 +235,27 @@ describe('createPluginActions', () => {
194235
)
195236
})
196237
})
238+
239+
describe('requestUserContext()', () => {
240+
it('send a message to the container to request the user info', () => {
241+
const { uid, postToContainer, onUpdateState } = mock()
242+
const {
243+
actions: { requestUserContext },
244+
} = createPluginActions({
245+
uid,
246+
postToContainer,
247+
onUpdateState,
248+
validateContent,
249+
})
250+
requestUserContext()
251+
expect(postToContainer).toHaveBeenLastCalledWith(
252+
expect.objectContaining({
253+
event: 'getUserContext',
254+
} satisfies Partial<GetUserContextMessage>),
255+
)
256+
})
257+
})
258+
197259
describe('selectAsset()', () => {
198260
it('send a message to the container to open the asset selector', () => {
199261
const { uid, postToContainer, onUpdateState } = mock()

packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginActions.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import {
44
assetFromAssetSelectedMessage,
55
assetModalChangeMessage,
66
getContextMessage,
7+
getUserContextMessage,
78
heightChangeMessage,
89
modalChangeMessage,
910
OnAssetSelectMessage,
1011
OnContextRequestMessage,
12+
OnUserContextRequestMessage,
1113
OnLoadedMessage,
1214
OnStateChangeMessage,
1315
OnUnknownPluginMessage,
@@ -62,6 +64,9 @@ export const createPluginActions: CreatePluginActions = ({
6264
const onContextRequest: OnContextRequestMessage = (data) => {
6365
popCallback('context', data.callbackId)?.(data)
6466
}
67+
const onUserContextRequest: OnUserContextRequestMessage = (data) => {
68+
popCallback('userContext', data.callbackId)?.(data)
69+
}
6570
const onAssetSelect: OnAssetSelectMessage = (data) => {
6671
popCallback('asset', data.callbackId)?.(data)
6772
}
@@ -81,6 +86,7 @@ export const createPluginActions: CreatePluginActions = ({
8186
onStateChange,
8287
onLoaded,
8388
onContextRequest,
89+
onUserContextRequest,
8490
onAssetSelect,
8591
onUnknownMessage,
8692
}
@@ -107,15 +113,20 @@ export const createPluginActions: CreatePluginActions = ({
107113
)
108114
})
109115
},
110-
setModalOpen: (isModalOpen) => {
116+
setModalOpen: (isModalOpen, modalSize) => {
111117
return new Promise((resolve) => {
112118
const callbackId = pushCallback('stateChanged', (message) =>
113119
resolve(
114120
pluginStateFromStateChangeMessage(message, validateContent),
115121
),
116122
)
117123
postToContainer(
118-
modalChangeMessage({ uid, callbackId, status: isModalOpen }),
124+
modalChangeMessage({
125+
uid,
126+
callbackId,
127+
status: isModalOpen,
128+
modalSize,
129+
}),
119130
)
120131
})
121132
},
@@ -135,6 +146,14 @@ export const createPluginActions: CreatePluginActions = ({
135146
postToContainer(getContextMessage({ uid, callbackId }))
136147
})
137148
},
149+
requestUserContext: () => {
150+
return new Promise((resolve) => {
151+
const callbackId = pushCallback('userContext', (message) =>
152+
resolve(message.user),
153+
)
154+
postToContainer(getUserContextMessage({ uid, callbackId }))
155+
})
156+
},
138157
},
139158
messageCallbacks,
140159
onHeightChange,

0 commit comments

Comments
 (0)