mirror of
https://github.com/qlik-oss/nebula.js.git
synced 2025-12-19 17:58:43 -05:00
chore: adjust bg and add tests (#971)
* chore: adjust bg and add tests * chore: add tests files * chore: temporary fix tests
This commit is contained in:
@@ -16,6 +16,7 @@ import useLayout, { useAppLayout } from '../hooks/useLayout';
|
|||||||
import InstanceContext from '../contexts/InstanceContext';
|
import InstanceContext from '../contexts/InstanceContext';
|
||||||
import useObjectSelections from '../hooks/useObjectSelections';
|
import useObjectSelections from '../hooks/useObjectSelections';
|
||||||
import eventmixin from '../selections/event-mixin';
|
import eventmixin from '../selections/event-mixin';
|
||||||
|
import { resolveBgColor, resolveBgImage } from '../utils/background-props';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
@@ -272,17 +273,6 @@ const loadType = async ({ dispatch, types, visualization, version, model, app, s
|
|||||||
return undefined;
|
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(
|
const Cell = forwardRef(
|
||||||
({ halo, model, initialSnOptions, initialSnPlugins, initialError, onMount, currentId }, ref) => {
|
({ halo, model, initialSnOptions, initialSnPlugins, initialError, onMount, currentId }, ref) => {
|
||||||
const { app, types } = halo;
|
const { app, types } = halo;
|
||||||
@@ -302,6 +292,8 @@ const Cell = forwardRef(
|
|||||||
const [hovering, setHover] = useState(false);
|
const [hovering, setHover] = useState(false);
|
||||||
const hoveringDebouncer = useRef({ enter: null, leave: null });
|
const hoveringDebouncer = useRef({ enter: null, leave: null });
|
||||||
const [bgColor, setBgColor] = useState(undefined);
|
const [bgColor, setBgColor] = useState(undefined);
|
||||||
|
const [bgImage, setBgImage] = useState(undefined); // {url: "", size: "", pos: ""}
|
||||||
|
|
||||||
const focusHandler = useRef({
|
const focusHandler = useRef({
|
||||||
focusToolbarButton(last) {
|
focusToolbarButton(last) {
|
||||||
// eslint-disable-next-line react/no-this-in-sfc
|
// eslint-disable-next-line react/no-this-in-sfc
|
||||||
@@ -314,13 +306,10 @@ const Cell = forwardRef(
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setBgColor(
|
const bgComp = layout?.components ? layout.components.find((comp) => comp.key === 'general') : null;
|
||||||
resolveBgColor(
|
setBgColor(resolveBgColor(bgComp, halo.public.theme));
|
||||||
layout && layout.components ? layout.components.find((comp) => comp.key === 'general') : null,
|
setBgImage(resolveBgImage(bgComp, halo.app));
|
||||||
halo.public.theme
|
}, [layout, halo.public.theme, halo.app]);
|
||||||
)
|
|
||||||
);
|
|
||||||
}, [layout, halo.public.theme]);
|
|
||||||
|
|
||||||
focusHandler.current.blurCallback = (resetFocus) => {
|
focusHandler.current.blurCallback = (resetFocus) => {
|
||||||
halo.root.toggleFocusOfCells();
|
halo.root.toggleFocusOfCells();
|
||||||
@@ -499,7 +488,17 @@ const Cell = forwardRef(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper
|
<Paper
|
||||||
style={{ position: 'relative', width: '100%', height: '100%', overflow: 'hidden' }}
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
overflow: 'hidden',
|
||||||
|
backgroundColor: bgColor,
|
||||||
|
backgroundImage: bgImage && bgImage.url ? `url(${bgImage.url})` : undefined,
|
||||||
|
backgroundRepeat: 'no-repeat',
|
||||||
|
backgroundSize: bgImage && bgImage.size,
|
||||||
|
backgroundPosition: bgImage && bgImage.pos,
|
||||||
|
}}
|
||||||
elevation={0}
|
elevation={0}
|
||||||
square
|
square
|
||||||
className={CellElement.className}
|
className={CellElement.className}
|
||||||
@@ -537,7 +536,6 @@ const Cell = forwardRef(
|
|||||||
xs
|
xs
|
||||||
style={{
|
style={{
|
||||||
height: '100%',
|
height: '100%',
|
||||||
backgroundColor: bgColor,
|
|
||||||
}}
|
}}
|
||||||
ref={contentRef}
|
ref={contentRef}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -19,89 +19,6 @@ const VizElement = {
|
|||||||
className: 'njs-viz',
|
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 }) {
|
function Supernova({ sn, snOptions: options, snPlugins: plugins, layout, appLayout, halo }) {
|
||||||
const { component } = sn;
|
const { component } = sn;
|
||||||
|
|
||||||
@@ -111,7 +28,6 @@ function Supernova({ sn, snOptions: options, snPlugins: plugins, layout, appLayo
|
|||||||
const [renderCnt, setRenderCnt] = useState(0);
|
const [renderCnt, setRenderCnt] = useState(0);
|
||||||
const [containerRef, containerRect, containerNode] = useRect();
|
const [containerRef, containerRect, containerNode] = useRect();
|
||||||
const [snNode, setSnNode] = useState(null);
|
const [snNode, setSnNode] = useState(null);
|
||||||
const [bgImage, setBgImage] = useState(undefined); // {url: "", size: "", pos: ""}
|
|
||||||
|
|
||||||
const snRef = useCallback((ref) => {
|
const snRef = useCallback((ref) => {
|
||||||
if (!ref) {
|
if (!ref) {
|
||||||
@@ -132,13 +48,6 @@ function Supernova({ sn, snOptions: options, snPlugins: plugins, layout, appLayo
|
|||||||
};
|
};
|
||||||
}, [snNode, component]);
|
}, [snNode, component]);
|
||||||
|
|
||||||
// Resolve Background Image
|
|
||||||
useEffect(() => {
|
|
||||||
setBgImage(
|
|
||||||
resolveBgImage(layout?.components ? layout.components.find((comp) => comp.key === 'general') : null, halo.app)
|
|
||||||
);
|
|
||||||
}, [layout, halo.app]);
|
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isMounted || !snNode || !containerRect) {
|
if (!isMounted || !snNode || !containerRect) {
|
||||||
@@ -218,10 +127,6 @@ function Supernova({ sn, snOptions: options, snPlugins: plugins, layout, appLayo
|
|||||||
style={{
|
style={{
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
backgroundImage: bgImage && bgImage.url ? `url(${bgImage.url})` : undefined,
|
|
||||||
backgroundRepeat: 'no-repeat',
|
|
||||||
backgroundSize: bgImage && bgImage.size,
|
|
||||||
backgroundPosition: bgImage && bgImage.pos,
|
|
||||||
}}
|
}}
|
||||||
className={VizElement.className}
|
className={VizElement.className}
|
||||||
>
|
>
|
||||||
|
|||||||
58
apis/nucleus/src/utils/__tests__/background-props.spec.js
Normal file
58
apis/nucleus/src/utils/__tests__/background-props.spec.js
Normal file
@@ -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');
|
||||||
|
});
|
||||||
|
});
|
||||||
91
apis/nucleus/src/utils/background-props.js
Normal file
91
apis/nucleus/src/utils/background-props.js
Normal file
@@ -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;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user