diff --git a/editor/src/components/editor/store/project-server-state.tsx b/editor/src/components/editor/store/project-server-state.tsx
index 67dc3284bf46..c2e6626c763a 100644
--- a/editor/src/components/editor/store/project-server-state.tsx
+++ b/editor/src/components/editor/store/project-server-state.tsx
@@ -6,6 +6,7 @@ import { updateProjectServerState } from '../actions/action-creators'
import { checkProjectOwned, projectIsStoredLocally } from '../persistence/persistence-backend'
import type { ProjectOwnership } from '../persistence/generic/persistence-types'
import { CollaborationEndpoints } from '../collaborative-endpoints'
+import { IS_TEST_ENVIRONMENT } from '../../../common/env-vars'
export interface ProjectMetadataFromServer {
title: string
@@ -163,7 +164,7 @@ export interface ProjectServerStateUpdaterProps {
}
let serverStateWatcherInstance: number | null = null
-const baseWatcherIntervalTime: number = 10 * 1000
+const baseWatcherIntervalTime: number = (IS_TEST_ENVIRONMENT ? 100 : 10) * 1000
let currentWatcherIntervalMultiplier: number = 1
function restartServerStateWatcher(
diff --git a/editor/src/components/inspector/sections/component-section/component-section.spec.browser2.tsx b/editor/src/components/inspector/sections/component-section/component-section.spec.browser2.tsx
index 5a4413891a86..5e6acc80502f 100644
--- a/editor/src/components/inspector/sections/component-section/component-section.spec.browser2.tsx
+++ b/editor/src/components/inspector/sections/component-section/component-section.spec.browser2.tsx
@@ -8,6 +8,7 @@ import {
DataPickerPopupTestId,
VariableFromScopeOptionTestId,
} from './component-section'
+import { ImagePreviewTestId } from './property-control-controls'
describe('Set element prop via the data picker', () => {
it('can pick from the property data picker', async () => {
@@ -320,3 +321,72 @@ registerExternalComponent(
],
},
)`
+
+const projectWithImage = (imageUrl: string) => `import * as React from 'react'
+import {
+ Storyboard,
+ Scene,
+ registerInternalComponent,
+} from 'utopia-api'
+
+function Image({ url }) {
+ return
+}
+
+var Playground = ({ style }) => {
+ return (
+
+
+
+ )
+}
+
+export var storyboard = (
+
+
+
+
+
+)
+
+registerInternalComponent(Image, {
+ supportsChildren: false,
+ properties: {
+ url: {
+ control: 'string-input',
+ },
+ },
+ variants: [
+ {
+ code: '',
+ },
+ ],
+})
+
+`
diff --git a/editor/src/components/inspector/sections/component-section/property-control-controls.tsx b/editor/src/components/inspector/sections/component-section/property-control-controls.tsx
index 3195eddc3cb0..d4077e38a76f 100644
--- a/editor/src/components/inspector/sections/component-section/property-control-controls.tsx
+++ b/editor/src/components/inspector/sections/component-section/property-control-controls.tsx
@@ -55,6 +55,7 @@ import {
normalisePathToUnderlyingTarget,
} from '../../../custom-code/code-file'
import { useDispatch } from '../../../editor/store/dispatch-context'
+import { isImage } from '../../../../core/shared/utils'
export interface ControlForPropProps {
propPath: PropertyPath
@@ -437,6 +438,8 @@ const NumberWithSliderControl = React.memo(
},
)
+export const ImagePreviewTestId = 'image-preview'
+
export const StringInputPropertyControl = React.memo(
(props: ControlForPropProps) => {
const { propName, propMetadata, controlDescription } = props
@@ -444,21 +447,59 @@ export const StringInputPropertyControl = React.memo(
const controlId = `${propName}-string-input-property-control`
const value = propMetadata.propertyStatus.set ? propMetadata.value : undefined
+ const safeValue = typeof value === 'string' ? value : ''
+
return (
-
+
+
+
+
)
},
)
+interface ImagePreviewProps {
+ url: string
+}
+const ImagePreview = React.memo(({ url }: ImagePreviewProps) => {
+ const [imageCanBeLoaded, setImageCanBeLoaded] = React.useState(isImage(url))
+
+ // we need to track if the url has changed so we retry loading the image even if it failed before
+ const urlRef = React.useRef(url)
+ if (urlRef.current !== url) {
+ setImageCanBeLoaded(isImage(url))
+ urlRef.current = url
+ }
+
+ // don't render the img when it can not be loaded
+ const onImageError = React.useCallback(() => {
+ setImageCanBeLoaded(false)
+ }, [setImageCanBeLoaded])
+
+ if (!imageCanBeLoaded) {
+ return null
+ }
+
+ return (
+
+ )
+})
+ImagePreview.displayName = 'ImagePreview'
+
function keysForVectorOfType(vectorType: 'vector2' | 'vector3' | 'vector4'): Array {
switch (vectorType) {
case 'vector2':