Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sandbox,lib,demo): modal in portal #416

Merged
merged 13 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/cli/templates/js/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ let previousType = 'loading'

// Establish communication with the Visual Editor
createFieldPlugin({
enablePortalModal: true,
validateContent: (content) => ({
content: typeof content === 'number' ? content : 0,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useFieldPlugin } from '@storyblok/field-plugin/react'

const FieldPlugin: FunctionComponent = () => {
const plugin = useFieldPlugin({
enablePortalModal: true,
/*
The `validateContent` parameter is optional. It allows you to
- validate the content
Expand Down
1 change: 1 addition & 0 deletions packages/cli/templates/vue2/src/fieldPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createFieldPlugin } from '@storyblok/field-plugin'
export const fieldPluginMixin = {
created() {
createFieldPlugin({
enablePortalModal: true,
validateContent: (content) => ({
content: typeof content === 'number' ? content : 0,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import AssetSelector from './AssetSelector.vue'
import { useFieldPlugin } from '@storyblok/field-plugin/vue3'

const plugin = useFieldPlugin({
enablePortalModal: true,
validateContent: (content: unknown) => ({
content: typeof content === 'number' ? content : 0,
}),
Expand Down
2 changes: 2 additions & 0 deletions packages/demo/src/components/FieldPluginDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export type PluginComponent = FunctionComponent<{
export const FieldPluginDemo: FunctionComponent = () => {
const { type, data, actions } = useFieldPlugin({
validateContent,
targetOrigin: 'http://localhost:7070',
johannes-lindgren marked this conversation as resolved.
Show resolved Hide resolved
enablePortalModal: true,
})

if (type === 'loading') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type CreateFieldPluginOptions<Content> = {
onUpdateState: (state: FieldPluginResponse<Content>) => void
validateContent?: ValidateContent<Content>
targetOrigin?: string
enablePortalModal?: boolean
}

export type CreateFieldPlugin = <Content = unknown>(
Expand All @@ -25,6 +26,7 @@ export const createFieldPlugin: CreateFieldPlugin = ({
onUpdateState,
validateContent,
targetOrigin,
enablePortalModal,
}) => {
const isEmbedded = window.parent !== window

Expand Down Expand Up @@ -107,6 +109,7 @@ export const createFieldPlugin: CreateFieldPlugin = ({
validateContent:
validateContent ||
((content) => ({ content: content as InferredContent })),
enablePortalModal,
})

const cleanupHeightChangeListener = createHeightChangeListener(onHeightChange)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type CreatePluginActions = <Content>(options: {
postToContainer: (message: unknown) => void
onUpdateState: (state: FieldPluginData<Content>) => void
validateContent: ValidateContent<Content>
enablePortalModal?: boolean
}) => {
// These functions are to be called by the field plugin when the user performs actions in the UI
actions: FieldPluginActions<Content>
Expand All @@ -46,6 +47,7 @@ export const createPluginActions: CreatePluginActions = ({
postToContainer,
onUpdateState,
validateContent,
enablePortalModal,
}) => {
const { pushCallback, popCallback } = callbackQueue()

Expand Down Expand Up @@ -143,7 +145,9 @@ export const createPluginActions: CreatePluginActions = ({
resolve(pluginStateFromStateChangeMessage(message, validateContent)),
)
// Request the initial state from the Visual Editor.
postToContainer(pluginLoadedMessage({ uid, callbackId }))
postToContainer(
pluginLoadedMessage({ uid, callbackId, enablePortalModal }),
)
})
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,96 @@ describe('PluginLoadedMessage', () => {
).toEqual(false)
})
})
describe('subscribeState', () => {
it('is optional', () => {
expect(
isPluginLoadedMessage({
...stub,
subscribeState: undefined,
}),
).toEqual(true)
})
it('is a boolean', () => {
expect(
isPluginLoadedMessage({
...stub,
subscribeState: true,
}),
).toEqual(true)
expect(
isPluginLoadedMessage({
...stub,
subscribeState: false,
}),
).toEqual(true)
const { subscribeState: _subscribeState, ...subWithoutSubscribeState } =
stub
expect(isPluginLoadedMessage(subWithoutSubscribeState)).toEqual(true)
expect(
isPluginLoadedMessage({
...stub,
subscribeState: 'false',
}),
).toEqual(false)
expect(
isPluginLoadedMessage({
...stub,
subscribeState: 123,
}),
).toEqual(false)
expect(
isPluginLoadedMessage({
...stub,
subscribeState: null,
}),
).toEqual(false)
})
})
describe('enablePortalModal', () => {
it('is optional', () => {
expect(
isPluginLoadedMessage({
...stub,
subscribeState: undefined,
}),
).toEqual(true)
})
it('is a boolean', () => {
expect(
isPluginLoadedMessage({
...stub,
subscribeState: true,
}),
).toEqual(true)
expect(
isPluginLoadedMessage({
...stub,
subscribeState: false,
}),
).toEqual(true)
const { subscribeState: _subscribeState, ...subWithoutSubscribeState } =
stub
expect(isPluginLoadedMessage(subWithoutSubscribeState)).toEqual(true)
expect(
isPluginLoadedMessage({
...stub,
subscribeState: 'false',
}),
).toEqual(false)
expect(
isPluginLoadedMessage({
...stub,
subscribeState: 123,
}),
).toEqual(false)
expect(
isPluginLoadedMessage({
...stub,
subscribeState: null,
}),
).toEqual(false)
})
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @johannes-lindgren 👋

Here, I think it should test the enablePortalModal right?

Copy link
Collaborator Author

@johannes-lindgren johannes-lindgren Oct 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I don't know what happened here, heh..

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

})
describe('constructor', () => {
it('includes the uid', () => {
Expand All @@ -90,5 +180,27 @@ describe('PluginLoadedMessage', () => {
true,
)
})
it('sets subscribeState to true', () => {
expect(pluginLoadedMessage({ uid, callbackId })).toHaveProperty(
'subscribeState',
true,
)
})
it('does not define enablePortalModal by default', () => {
expect(pluginLoadedMessage({ uid, callbackId })).not.toHaveProperty(
'enablePortalModal',
)
})
it('lets you define enablePortalModal', () => {
expect(
pluginLoadedMessage({ uid, callbackId, enablePortalModal: true }),
).toHaveProperty('enablePortalModal', true)
expect(
pluginLoadedMessage({ uid, callbackId, enablePortalModal: false }),
).toHaveProperty('enablePortalModal', false)
expect(
pluginLoadedMessage({ uid, callbackId, enablePortalModal: undefined }),
).toHaveProperty('enablePortalModal', undefined)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type PluginLoadedMessage = MessageToContainer<'loaded'> & {
// signals that the field plugin is responsible for its own scrolling in modal mode
fullHeight?: boolean
subscribeState?: boolean
enablePortalModal?: boolean
}
export const isPluginLoadedMessage = (
obj: unknown,
Expand All @@ -16,10 +17,16 @@ export const isPluginLoadedMessage = (
typeof obj.fullHeight === 'boolean') &&
(!hasKey(obj, 'subscribeState') ||
typeof obj.subscribeState === 'undefined' ||
typeof obj.subscribeState === 'boolean')
typeof obj.subscribeState === 'boolean') &&
(!hasKey(obj, 'enablePortalModal') ||
typeof obj.enablePortalModal === 'undefined' ||
typeof obj.enablePortalModal === 'boolean')

export const pluginLoadedMessage = (
options: Pick<PluginLoadedMessage, 'uid' | 'callbackId'>,
options: Pick<
PluginLoadedMessage,
'uid' | 'callbackId' | 'enablePortalModal'
>,
): PluginLoadedMessage => ({
action: 'plugin-changed',
event: 'loaded',
Expand Down
29 changes: 24 additions & 5 deletions packages/sandbox/src/components/FieldPluginSandbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ const defaultManifest = { options: [] }
const urlQueryParam = withDefault(StringParam, defaultUrl)
const manifestQueryParam = withDefault(JsonParam, defaultManifest)

export type ModalState =
| 'non-modal'
| 'modal-with-portal'
| 'modal-without-portal'

const useSandbox = (
onError: (message: { title: string; message?: string }) => void,
) => {
Expand Down Expand Up @@ -108,6 +113,7 @@ const useSandbox = (
const [isModalOpen, setModalOpen] = useState(false)
const [height, setHeight] = useState(initialHeight)
const [fullHeight, setFullHeight] = useState(false)
const [enablePortalModal, setEnablePortalModal] = useState(false)
const [schema, setSchema] = useState<FieldPluginSchema>({
field_type: 'preview',
options: manifest.options,
Expand Down Expand Up @@ -218,6 +224,7 @@ const useSandbox = (
(message: PluginLoadedMessage) => {
setSubscribeState(Boolean(message.subscribeState))
setFullHeight(Boolean(message.fullHeight))
setEnablePortalModal(Boolean(message.enablePortalModal))
dispatchLoadedChanged({
...stateChangedData,
action: 'loaded',
Expand Down Expand Up @@ -302,13 +309,23 @@ const useSandbox = (
],
)

const modalState = useMemo<ModalState>(() => {
if (!isModalOpen) {
return 'non-modal'
} else if (enablePortalModal) {
return 'modal-with-portal'
} else {
return 'modal-without-portal'
}
}, [isModalOpen, enablePortalModal])

return [
{
content,
language,
isModalOpen,
height,
fullHeight,
modalState,
schema,
url,
fieldTypeIframe,
Expand All @@ -320,6 +337,7 @@ const useSandbox = (
setSchema,
setUrl,
randomizeUid,
setModalOpen,
},
] as const
}
Expand All @@ -330,15 +348,15 @@ export const FieldPluginSandbox: FunctionComponent = () => {
{
content,
language,
isModalOpen,
modalState,
fullHeight,
height,
schema,
url,
fieldTypeIframe,
iframeSrc,
},
{ setContent, setLanguage, setSchema, setUrl, randomizeUid },
{ setModalOpen, setContent, setLanguage, setSchema, setUrl, randomizeUid },
] = useSandbox(error)

return (
Expand Down Expand Up @@ -370,9 +388,10 @@ export const FieldPluginSandbox: FunctionComponent = () => {
<FieldTypePreview
src={iframeSrc}
height={height}
isModal={isModalOpen}
modalState={modalState}
fullHeight={fullHeight}
ref={fieldTypeIframe}
onModalChange={setModalOpen}
/>
</CenteredContent>
<Stack alignSelf="flex-start">
Expand Down Expand Up @@ -444,7 +463,7 @@ export const FieldPluginSandbox: FunctionComponent = () => {
output={
{
content,
isModalOpen,
isModalOpen: modalState !== 'non-modal',
translatable: schema.translatable,
storyLang: language,
options: recordFromFieldPluginOptions(schema.options),
Expand Down
Loading
Loading