From 3a8c48d5642b51a3c3602ee0a98c2e9646b555cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20=C3=85str=C3=B6m?= Date: Fri, 21 Oct 2022 12:58:28 +0200 Subject: [PATCH] chore: adjust bg and add tests (#971) * chore: adjust bg and add tests * chore: add tests files * chore: temporary fix tests --- apis/nucleus/src/components/Cell.jsx | 38 ++++---- apis/nucleus/src/components/Supernova.jsx | 95 ------------------- .../utils/__tests__/background-props.spec.js | 58 +++++++++++ apis/nucleus/src/utils/background-props.js | 91 ++++++++++++++++++ 4 files changed, 167 insertions(+), 115 deletions(-) create mode 100644 apis/nucleus/src/utils/__tests__/background-props.spec.js create mode 100644 apis/nucleus/src/utils/background-props.js diff --git a/apis/nucleus/src/components/Cell.jsx b/apis/nucleus/src/components/Cell.jsx index 2e54474d0..3e2f34973 100644 --- a/apis/nucleus/src/components/Cell.jsx +++ b/apis/nucleus/src/components/Cell.jsx @@ -16,6 +16,7 @@ import useLayout, { useAppLayout } from '../hooks/useLayout'; import InstanceContext from '../contexts/InstanceContext'; import useObjectSelections from '../hooks/useObjectSelections'; import eventmixin from '../selections/event-mixin'; +import { resolveBgColor, resolveBgImage } from '../utils/background-props'; /** * @interface @@ -272,17 +273,6 @@ const loadType = async ({ dispatch, types, visualization, version, model, app, s return undefined; }; -const resolveBgColor = (bgComp, theme) => { - const bgColor = bgComp?.bgColor; - if (bgColor && theme) { - if (bgColor.useColorExpression) { - return theme.validateColor(bgColor.colorExpression); - } - return bgColor.color && bgColor.color.color !== 'none' ? theme.getColorPickerColor(bgColor.color) : undefined; - } - return undefined; -}; - const Cell = forwardRef( ({ halo, model, initialSnOptions, initialSnPlugins, initialError, onMount, currentId }, ref) => { const { app, types } = halo; @@ -302,6 +292,8 @@ const Cell = forwardRef( const [hovering, setHover] = useState(false); const hoveringDebouncer = useRef({ enter: null, leave: null }); const [bgColor, setBgColor] = useState(undefined); + const [bgImage, setBgImage] = useState(undefined); // {url: "", size: "", pos: ""} + const focusHandler = useRef({ focusToolbarButton(last) { // eslint-disable-next-line react/no-this-in-sfc @@ -314,13 +306,10 @@ const Cell = forwardRef( }, []); useEffect(() => { - setBgColor( - resolveBgColor( - layout && layout.components ? layout.components.find((comp) => comp.key === 'general') : null, - halo.public.theme - ) - ); - }, [layout, halo.public.theme]); + const bgComp = layout?.components ? layout.components.find((comp) => comp.key === 'general') : null; + setBgColor(resolveBgColor(bgComp, halo.public.theme)); + setBgImage(resolveBgImage(bgComp, halo.app)); + }, [layout, halo.public.theme, halo.app]); focusHandler.current.blurCallback = (resetFocus) => { halo.root.toggleFocusOfCells(); @@ -499,7 +488,17 @@ const Cell = forwardRef( return ( diff --git a/apis/nucleus/src/components/Supernova.jsx b/apis/nucleus/src/components/Supernova.jsx index 4e0ed100c..748f841bd 100644 --- a/apis/nucleus/src/components/Supernova.jsx +++ b/apis/nucleus/src/components/Supernova.jsx @@ -19,89 +19,6 @@ const VizElement = { className: 'njs-viz', }; -const imageSizingToCssProperty = { - originalSize: 'auto auto', - alwaysFit: 'contain', - fitWidth: '100% auto', - fitHeight: 'auto 100%', - stretchFit: '100% 100%', - alwaysFill: 'cover', -}; - -const positionToCss = { - 'top-left': 'top left', - 'top-center': 'top center', - 'top-right': 'top right', - 'center-left': 'center left', - 'center-center': 'center center', - 'center-right': 'center right', - 'bottom-left': 'bottom left', - 'bottom-center': 'bottom center', - 'bottom-right': 'bottom right', -}; - -// TODO: this needs some proper verification -function getSenseServerUrl(app) { - let config; - let wsUrl; - let protocol; - let isSecure; - - if (app?.session?.config) { - config = app.session.config; - wsUrl = new URL(config.url); - - isSecure = wsUrl.protocol === 'wss:'; - protocol = isSecure ? 'https://' : 'http://'; - return protocol + wsUrl.host; - } - return undefined; -} - -function getBackgroundPosition(bgComp) { - let bkgImagePosition = 'center center'; - if (bgComp?.bgImage?.position) { - bkgImagePosition = positionToCss[bgComp.bgImage.position]; - } - return bkgImagePosition; -} - -function getBackgroundSize(bgComp) { - let bkgImageSize = imageSizingToCssProperty.originalSize; - if (bgComp?.bgImage?.sizing) { - bkgImageSize = imageSizingToCssProperty[bgComp.bgImage.sizing]; - } - return bkgImageSize; -} - -function resolveImageUrl(app, relativeUrl) { - return relativeUrl ? getSenseServerUrl(app) + relativeUrl : undefined; -} - -const resolveBgImage = (bgComp, app) => { - const bgImageDef = bgComp?.bgImage; - - if (bgImageDef) { - let url = ''; - if (bgImageDef.mode === 'media') { - url = bgImageDef?.mediaUrl?.qStaticContentUrl?.qUrl - ? decodeURIComponent(bgImageDef.mediaUrl.qStaticContentUrl.qUrl) - : undefined; - url = resolveImageUrl(app, url); - } - if (bgImageDef.mode === 'expression') { - url = bgImageDef.expressionUrl ? decodeURIComponent(bgImageDef.expressionUrl) : undefined; - } - const pos = getBackgroundPosition(bgComp); - const size = getBackgroundSize(bgComp); - - // TODO: need to resolve the URL by the WS path - - return url ? { url, pos, size } : undefined; - } - return undefined; -}; - function Supernova({ sn, snOptions: options, snPlugins: plugins, layout, appLayout, halo }) { const { component } = sn; @@ -111,7 +28,6 @@ function Supernova({ sn, snOptions: options, snPlugins: plugins, layout, appLayo const [renderCnt, setRenderCnt] = useState(0); const [containerRef, containerRect, containerNode] = useRect(); const [snNode, setSnNode] = useState(null); - const [bgImage, setBgImage] = useState(undefined); // {url: "", size: "", pos: ""} const snRef = useCallback((ref) => { if (!ref) { @@ -132,13 +48,6 @@ function Supernova({ sn, snOptions: options, snPlugins: plugins, layout, appLayo }; }, [snNode, component]); - // Resolve Background Image - useEffect(() => { - setBgImage( - resolveBgImage(layout?.components ? layout.components.find((comp) => comp.key === 'general') : null, halo.app) - ); - }, [layout, halo.app]); - // Render useEffect(() => { if (!isMounted || !snNode || !containerRect) { @@ -218,10 +127,6 @@ function Supernova({ sn, snOptions: options, snPlugins: plugins, layout, appLayo style={{ position: 'relative', height: '100%', - backgroundImage: bgImage && bgImage.url ? `url(${bgImage.url})` : undefined, - backgroundRepeat: 'no-repeat', - backgroundSize: bgImage && bgImage.size, - backgroundPosition: bgImage && bgImage.pos, }} className={VizElement.className} > diff --git a/apis/nucleus/src/utils/__tests__/background-props.spec.js b/apis/nucleus/src/utils/__tests__/background-props.spec.js new file mode 100644 index 000000000..8b8a5eb9a --- /dev/null +++ b/apis/nucleus/src/utils/__tests__/background-props.spec.js @@ -0,0 +1,58 @@ +import theme from '@nebula.js/theme'; +import { resolveBgColor, resolveBgImage } from '../background-props'; + +describe('Background property resolver', () => { + let bgCompLayout; + + const t = theme().externalAPI; + + const app = { + session: { + config: { + url: 'wss://example.com/lots/of/paths', + }, + }, + }; + + beforeEach(() => { + bgCompLayout = { + key: 'general', + bgColor: { + useColorExpression: true, + colorExpression: '#ff0000', + color: { index: -1, color: 'aqua' }, + }, + bgImage: { + mode: 'media', + mediaUrl: { + qStaticContentUrl: { + qUrl: '/media/Tulips.jpg', + }, + }, + position: 'top-left', + sizing: 'alwaysFit', + }, + }; + }); + + it('should resolve background color by expression', () => { + const color = resolveBgColor(bgCompLayout, t); + expect(color).to.equal('rgb(255, 0, 0)'); + }); + it('should resolve background color by picker', () => { + bgCompLayout.bgColor.useColorExpression = false; + const color = resolveBgColor(bgCompLayout, t); + expect(color).to.equal('aqua'); + }); + it('should resolve background image https', () => { + const { url, pos } = resolveBgImage(bgCompLayout, app); + expect(url).to.equal('https://example.com/media/Tulips.jpg'); + expect(pos).to.equal('top left'); + }); + it('should resolve background image http', () => { + app.session.config.url = 'ws://example.com/lots/of/paths'; + const { url, size } = resolveBgImage(bgCompLayout, app); + expect(url).to.equal('http://example.com/media/Tulips.jpg'); + expect(size).to.equal('contain'); + }); +}); diff --git a/apis/nucleus/src/utils/background-props.js b/apis/nucleus/src/utils/background-props.js new file mode 100644 index 000000000..57b0bf25e --- /dev/null +++ b/apis/nucleus/src/utils/background-props.js @@ -0,0 +1,91 @@ +const imageSizingToCssProperty = { + originalSize: 'auto auto', + alwaysFit: 'contain', + fitWidth: '100% auto', + fitHeight: 'auto 100%', + stretchFit: '100% 100%', + alwaysFill: 'cover', +}; + +const positionToCss = { + 'top-left': 'top left', + 'top-center': 'top center', + 'top-right': 'top right', + 'center-left': 'center left', + 'center-center': 'center center', + 'center-right': 'center right', + 'bottom-left': 'bottom left', + 'bottom-center': 'bottom center', + 'bottom-right': 'bottom right', +}; + +// TODO: this needs some proper verification +function getSenseServerUrl(app) { + let config; + let wsUrl; + let protocol; + let isSecure; + + if (app?.session?.config) { + config = app.session.config; + wsUrl = new URL(config.url); + + isSecure = wsUrl.protocol === 'wss:'; + protocol = isSecure ? 'https://' : 'http://'; + return protocol + wsUrl.host; + } + return undefined; +} + +function getBackgroundPosition(bgComp) { + let bkgImagePosition = 'center center'; + if (bgComp?.bgImage?.position) { + bkgImagePosition = positionToCss[bgComp.bgImage.position]; + } + return bkgImagePosition; +} + +function getBackgroundSize(bgComp) { + let bkgImageSize = imageSizingToCssProperty.originalSize; + if (bgComp?.bgImage?.sizing) { + bkgImageSize = imageSizingToCssProperty[bgComp.bgImage.sizing]; + } + return bkgImageSize; +} + +function resolveImageUrl(app, relativeUrl) { + return relativeUrl ? getSenseServerUrl(app) + relativeUrl : undefined; +} + +export const resolveBgImage = (bgComp, app) => { + const bgImageDef = bgComp?.bgImage; + + if (bgImageDef) { + let url = ''; + if (bgImageDef.mode === 'media') { + url = bgImageDef?.mediaUrl?.qStaticContentUrl?.qUrl + ? decodeURIComponent(bgImageDef.mediaUrl.qStaticContentUrl.qUrl) + : undefined; + url = resolveImageUrl(app, url); + } + if (bgImageDef.mode === 'expression') { + url = bgImageDef.expressionUrl ? decodeURIComponent(bgImageDef.expressionUrl) : undefined; + } + const pos = getBackgroundPosition(bgComp); + const size = getBackgroundSize(bgComp); + + return url ? { url, pos, size } : undefined; + } + return undefined; +}; + +export const resolveBgColor = (bgComp, theme) => { + const bgColor = bgComp?.bgColor; + if (bgColor && theme) { + if (bgColor.useColorExpression) { + return theme.validateColor(bgColor.colorExpression); + } + return bgColor.color && bgColor.color.color !== 'none' ? theme.getColorPickerColor(bgColor.color) : undefined; + } + return undefined; +};