move static presentationUtil code into package @kbn/expression-utils (#213659)

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Nathan Reese 2025-03-11 14:24:00 -06:00 committed by GitHub
parent f079d964e2
commit 99d8400328
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 121 additions and 134 deletions

1
.github/CODEOWNERS vendored
View file

@ -317,6 +317,7 @@ src/platform/packages/private/kbn-ci-stats-reporter @elastic/kibana-operations
src/platform/packages/private/kbn-code-owners @elastic/appex-qa
src/platform/packages/private/kbn-config-mocks @elastic/kibana-core
src/platform/packages/private/kbn-esql-editor @elastic/kibana-esql
src/platform/packages/private/kbn-expression-utils @elastic/kibana-presentation
src/platform/packages/private/kbn-ftr-screenshot-filename @elastic/kibana-operations @elastic/appex-qa
src/platform/packages/private/kbn-gen-ai-functional-testing @elastic/appex-ai-infra
src/platform/packages/private/kbn-generate-csv @elastic/appex-sharedux

View file

@ -524,6 +524,7 @@
"@kbn/expression-reveal-image-plugin": "link:src/platform/plugins/shared/expression_reveal_image",
"@kbn/expression-shape-plugin": "link:src/platform/plugins/shared/expression_shape",
"@kbn/expression-tagcloud-plugin": "link:src/platform/plugins/shared/chart_expressions/expression_tagcloud",
"@kbn/expression-utils": "link:src/platform/packages/private/kbn-expression-utils",
"@kbn/expression-xy-plugin": "link:src/platform/plugins/shared/chart_expressions/expression_xy",
"@kbn/expressions-explorer-plugin": "link:examples/expressions_explorer",
"@kbn/expressions-plugin": "link:src/platform/plugins/shared/expressions",

View file

@ -120,7 +120,7 @@ pageLoadAssetSize:
osquery: 107090
painlessLab: 179748
presentationPanel: 11550
presentationUtil: 33905
presentationUtil: 9000
productDocBase: 22500
profiling: 36694
remoteClusters: 51327

View file

@ -0,0 +1,3 @@
# @kbn/expression-utils
Empty package generated by @kbn/generate

View file

@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
export {
elasticLogo,
elasticOutline,
isValidUrl,
isValidHttpUrl,
resolveWithMissingImage,
resolveFromArgs,
encode,
parseDataUrl,
} from './src';

View file

@ -7,4 +7,8 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
export * from './utils';
module.exports = {
preset: '@kbn/test/jest_node',
rootDir: '../../../../..',
roots: ['<rootDir>/src/platform/packages/private/kbn-expression-utils'],
};

View file

@ -0,0 +1,7 @@
{
"type": "shared-common",
"id": "@kbn/expression-utils",
"owner": "@elastic/kibana-presentation",
"group": "platform",
"visibility": "private"
}

View file

@ -0,0 +1,6 @@
{
"name": "@kbn/expression-utils",
"private": true,
"version": "1.0.0",
"license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0"
}

View file

@ -8,14 +8,8 @@
*/
export * from './dataurl';
export { elasticLogo } from './elastic_logo';
export { elasticOutline } from './elastic_outline';
export * from './httpurl';
export * from './resolve_dataurl';
export * from './url';
export async function getElasticLogo() {
return await import('./elastic_logo');
}
export async function getElasticOutline() {
return await import('./elastic_outline');
}

View file

@ -0,0 +1,17 @@
{
"extends": "../../../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node"
]
},
"include": [
"**/*.ts",
],
"exclude": [
"target/**/*"
],
"kbn_references": []
}

View file

@ -9,21 +9,13 @@
import expect from '@kbn/expect';
import { ExecutionContext } from '@kbn/expressions-plugin/common';
import { getElasticLogo, getElasticOutline } from '@kbn/presentation-util-plugin/common';
import { elasticLogo, elasticOutline } from '@kbn/expression-utils';
import { functionWrapper } from '@kbn/presentation-util-plugin/test_helpers';
import { imageFunction as image } from './image_function';
describe('image', () => {
const fn = functionWrapper(image);
let elasticLogo: string;
let elasticOutline: string;
beforeEach(async () => {
elasticLogo = (await getElasticLogo())?.elasticLogo;
elasticOutline = (await getElasticOutline())?.elasticOutline;
});
it('returns an image object using a dataUrl', async () => {
const result = await fn(
null,

View file

@ -8,7 +8,6 @@
*/
import { i18n } from '@kbn/i18n';
import { getElasticLogo, resolveWithMissingImage } from '@kbn/presentation-util-plugin/common';
import { BASE64, URL } from '../constants';
import { ExpressionImageFunction, ImageMode } from '../types';
@ -85,8 +84,8 @@ export const imageFunction: ExpressionImageFunction = () => {
throw new Error(errors.invalidImageMode());
}
const { elasticLogo, resolveWithMissingImage } = await import('@kbn/expression-utils');
const modeStyle = mode === 'stretch' ? '100% 100%' : mode;
const { elasticLogo } = await getElasticLogo();
return {
type: 'image',
mode: modeStyle,

View file

@ -10,12 +10,12 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { coreMock } from '@kbn/core/public/mocks';
import { Render, waitFor } from '@kbn/presentation-util-plugin/public/__stories__';
import { getElasticLogo } from '@kbn/presentation-util-plugin/common';
import { Render } from '@kbn/presentation-util-plugin/public/__stories__';
import { elasticLogo } from '@kbn/expression-utils';
import { getImageRenderer } from '../image_renderer';
import { ImageMode } from '../../../common';
const Renderer = ({ elasticLogo }: { elasticLogo: string }) => {
const Renderer = () => {
const config = {
dataurl: elasticLogo,
mode: ImageMode.COVER,
@ -31,10 +31,6 @@ const Renderer = ({ elasticLogo }: { elasticLogo: string }) => {
);
};
storiesOf('renderers/image', module).add(
'default',
(_, props) => {
return <Renderer elasticLogo={props?.elasticLogo} />;
},
{ decorators: [waitFor(getElasticLogo())] }
);
storiesOf('renderers/image', module).add('default', (_, props) => {
return <Renderer />;
});

View file

@ -16,7 +16,6 @@ import {
IInterpreterRenderHandlers,
} from '@kbn/expressions-plugin/common';
import { i18n } from '@kbn/i18n';
import { getElasticLogo, isValidUrl } from '@kbn/presentation-util-plugin/common';
import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme';
import { KibanaErrorBoundary, KibanaErrorBoundaryProvider } from '@kbn/shared-ux-error-boundary';
import { ImageRendererConfig } from '../../common/types';
@ -43,7 +42,7 @@ export const getImageRenderer =
config: ImageRendererConfig,
handlers: IInterpreterRenderHandlers
) => {
const { elasticLogo } = await getElasticLogo();
const { elasticLogo, isValidUrl } = await import('@kbn/expression-utils');
const dataurl = isValidUrl(config.dataurl ?? '') ? config.dataurl : elasticLogo;
const style = {

View file

@ -18,6 +18,7 @@
"@kbn/i18n",
"@kbn/react-kibana-context-theme",
"@kbn/shared-ux-error-boundary",
"@kbn/expression-utils",
],
"exclude": [
"target/**/*",

View file

@ -8,20 +8,13 @@
*/
import { ExecutionContext } from '@kbn/expressions-plugin/common';
import { getElasticLogo, getElasticOutline } from '@kbn/presentation-util-plugin/common';
import { elasticLogo, elasticOutline } from '@kbn/expression-utils';
import { functionWrapper } from '@kbn/presentation-util-plugin/test_helpers';
import { repeatImageFunction } from './repeat_image_function';
describe('repeatImage', () => {
const fn = functionWrapper(repeatImageFunction);
let elasticLogo: string;
let elasticOutline: string;
beforeEach(async () => {
elasticLogo = await (await getElasticLogo()).elasticLogo;
elasticOutline = await (await getElasticOutline()).elasticOutline;
});
it('returns a render as repeatImage', async () => {
const result = await fn(10, {}, {} as ExecutionContext);
expect(result).toHaveProperty('type', 'render');

View file

@ -8,11 +8,6 @@
*/
import { i18n } from '@kbn/i18n';
import {
getElasticOutline,
isValidUrl,
resolveWithMissingImage,
} from '@kbn/presentation-util-plugin/common';
import { CONTEXT, BASE64, URL } from '../constants';
import { ExpressionRepeatImageFunction } from '../types';
@ -97,10 +92,12 @@ export const repeatImageFunction: ExpressionRepeatImageFunction = () => {
},
},
fn: async (count, args) => {
const { elasticOutline, isValidUrl, resolveWithMissingImage } = await import(
'@kbn/expression-utils'
);
if (args.emptyImage !== null && isValidUrl(args.emptyImage) && args.max === null) {
throw new Error(errors.getMissingMaxArgumentErrorMessage());
}
const { elasticOutline } = await getElasticOutline();
return {
type: 'render',
as: 'repeatImage',

View file

@ -11,17 +11,10 @@ import React from 'react';
import { storiesOf } from '@storybook/react';
import { coreMock } from '@kbn/core/public/mocks';
import { Render } from '@kbn/presentation-util-plugin/public/__stories__';
import { getElasticLogo, getElasticOutline } from '@kbn/presentation-util-plugin/common';
import { waitFor } from '@kbn/presentation-util-plugin/public/__stories__';
import { elasticLogo, elasticOutline } from '@kbn/expression-utils';
import { getRepeatImageRenderer } from '../repeat_image_renderer';
const Renderer = ({
elasticLogo,
elasticOutline,
}: {
elasticLogo: string;
elasticOutline: string;
}) => {
const Renderer = () => {
const config = {
count: 42,
image: elasticLogo,
@ -39,10 +32,6 @@ const Renderer = ({
);
};
storiesOf('enderers/repeatImage', module).add(
'default',
(_, props) => (
<Renderer elasticLogo={props?.elasticLogo} elasticOutline={props?.elasticOutline} />
),
{ decorators: [waitFor(getElasticLogo()), waitFor(getElasticOutline())] }
);
storiesOf('enderers/repeatImage', module).add('default', (_, props) => <Renderer />, {
decorators: [],
});

View file

@ -19,7 +19,6 @@ import { i18n } from '@kbn/i18n';
import { I18nProvider } from '@kbn/i18n-react';
import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme';
import { KibanaErrorBoundary, KibanaErrorBoundaryProvider } from '@kbn/shared-ux-error-boundary';
import { getElasticOutline, isValidUrl } from '@kbn/presentation-util-plugin/common';
import { RepeatImageRendererConfig } from '../../common/types';
const strings = {
@ -44,8 +43,10 @@ export const getRepeatImageRenderer =
config: RepeatImageRendererConfig,
handlers: IInterpreterRenderHandlers
) => {
const { RepeatImageComponent } = await import('../components/repeat_image_component');
const { elasticOutline } = await getElasticOutline();
const [{ elasticOutline, isValidUrl }, { RepeatImageComponent }] = await Promise.all([
import('@kbn/expression-utils'),
import('../components/repeat_image_component'),
]);
const settings = {
...config,
image: isValidUrl(config.image) ? config.image : elasticOutline,

View file

@ -17,6 +17,7 @@
"@kbn/react-kibana-context-theme",
"@kbn/i18n-react",
"@kbn/shared-ux-error-boundary",
"@kbn/expression-utils"
],
"exclude": [
"target/**/*",

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { getElasticOutline, getElasticLogo } from '@kbn/presentation-util-plugin/common';
import { elasticOutline, elasticLogo } from '@kbn/expression-utils';
import { functionWrapper } from '@kbn/presentation-util-plugin/test_helpers';
import { revealImageFunction, errors } from './reveal_image_function';
import { Origin } from '../types';
@ -16,14 +16,6 @@ import { ExecutionContext } from '@kbn/expressions-plugin/common';
describe('revealImageFunction', () => {
const fn = functionWrapper(revealImageFunction);
let elasticLogo = '';
let elasticOutline = '';
beforeEach(async () => {
elasticLogo = (await getElasticLogo()).elasticLogo;
elasticOutline = (await getElasticOutline()).elasticOutline;
});
it('returns a render as revealImage', async () => {
const result = await fn(
0.5,

View file

@ -8,11 +8,6 @@
*/
import { i18n } from '@kbn/i18n';
import {
resolveWithMissingImage,
getElasticOutline,
isValidUrl,
} from '@kbn/presentation-util-plugin/common';
import { ExpressionRevealImageFunction, Origin, Position } from '../types';
import { BASE64, URL } from '../constants';
@ -108,11 +103,14 @@ export const revealImageFunction: ExpressionRevealImageFunction = () => {
throw errors.invalidPercent(percent);
}
const { resolveWithMissingImage, elasticOutline, isValidUrl } = await import(
'@kbn/expression-utils'
);
if (args.image && !isValidUrl(args.image)) {
throw errors.invalidImageUrl(args.image);
}
const { elasticOutline } = await getElasticOutline();
return {
type: 'render',
as: 'revealImage',

View file

@ -11,7 +11,7 @@ import React, { useRef, useState, useEffect, useCallback } from 'react';
import { useResizeObserver } from '@elastic/eui';
import { IInterpreterRenderHandlers } from '@kbn/expressions-plugin/common';
import { css, CSSObject } from '@emotion/react';
import { isValidUrl } from '@kbn/presentation-util-plugin/common';
import { isValidUrl } from '@kbn/expression-utils';
import { NodeDimensions, RevealImageRendererConfig, OriginString } from '../../common/types';
const revealImageParentStyle = css`

View file

@ -10,18 +10,12 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { coreMock } from '@kbn/core/public/mocks';
import { getElasticOutline, getElasticLogo } from '@kbn/presentation-util-plugin/common';
import { Render, waitFor } from '@kbn/presentation-util-plugin/public/__stories__';
import { elasticOutline, elasticLogo } from '@kbn/expression-utils';
import { Render } from '@kbn/presentation-util-plugin/public/__stories__';
import { getRevealImageRenderer } from '..';
import { Origin } from '../../../common/types/expression_functions';
const Renderer = ({
elasticLogo,
elasticOutline,
}: {
elasticLogo: string;
elasticOutline: string;
}) => {
const Renderer = () => {
const config = {
image: elasticLogo,
emptyImage: elasticOutline,
@ -32,10 +26,6 @@ const Renderer = ({
return <Render renderer={getRevealImageRenderer(coreMock.createStart())} config={config} />;
};
storiesOf('renderers/revealImage', module).add(
'default',
(_, props) => (
<Renderer elasticLogo={props?.elasticLogo} elasticOutline={props?.elasticOutline} />
),
{ decorators: [waitFor(getElasticLogo()), waitFor(getElasticOutline())] }
);
storiesOf('renderers/revealImage', module).add('default', (_, props) => <Renderer />, {
decorators: [],
});

View file

@ -17,6 +17,7 @@
"@kbn/react-kibana-context-theme",
"@kbn/i18n-react",
"@kbn/shared-ux-error-boundary",
"@kbn/expression-utils"
],
"exclude": [
"target/**/*",

View file

@ -34,14 +34,3 @@ export {
getProjectIDs,
isProjectEnabledByStatus,
} from './labs';
export {
getElasticLogo,
getElasticOutline,
isValidUrl,
isValidHttpUrl,
resolveWithMissingImage,
resolveFromArgs,
encode,
parseDataUrl,
} from './lib';

View file

@ -934,6 +934,8 @@
"@kbn/expression-shape-plugin/*": ["src/platform/plugins/shared/expression_shape/*"],
"@kbn/expression-tagcloud-plugin": ["src/platform/plugins/shared/chart_expressions/expression_tagcloud"],
"@kbn/expression-tagcloud-plugin/*": ["src/platform/plugins/shared/chart_expressions/expression_tagcloud/*"],
"@kbn/expression-utils": ["src/platform/packages/private/kbn-expression-utils"],
"@kbn/expression-utils/*": ["src/platform/packages/private/kbn-expression-utils/*"],
"@kbn/expression-xy-plugin": ["src/platform/plugins/shared/chart_expressions/expression_xy"],
"@kbn/expression-xy-plugin/*": ["src/platform/plugins/shared/chart_expressions/expression_xy/*"],
"@kbn/expressions-explorer-plugin": ["examples/expressions_explorer"],

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { getElasticLogo } from '@kbn/presentation-util-plugin/common';
import { elasticLogo } from '@kbn/expression-utils';
export const fontStyle = {
type: 'style',
@ -23,7 +23,6 @@ export const fontStyle = {
};
export const getContainerStyle = async () => {
const { elasticLogo } = await getElasticLogo();
return {
type: 'containerStyle',
border: '3px dotted blue',

View file

@ -6,7 +6,7 @@
*/
import { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common';
import { isValidUrl } from '@kbn/presentation-util-plugin/common';
import { isValidUrl } from '@kbn/expression-utils';
import { ContainerStyle, Overflow, BackgroundRepeat, BackgroundSize } from '../../../types';
import { getFunctionHelp, getFunctionErrors } from '../../../i18n';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { getElasticLogo } from '@kbn/presentation-util-plugin/common';
import { elasticLogo } from '@kbn/expression-utils';
import { functionWrapper } from '@kbn/presentation-util-plugin/test_helpers';
import { getFunctionErrors } from '../../../i18n';
import { containerStyle } from './containerStyle';
@ -15,11 +15,6 @@ const errors = getFunctionErrors().containerStyle;
describe('containerStyle', () => {
const fn = functionWrapper(containerStyle);
let elasticLogo;
beforeEach(async () => {
elasticLogo = (await getElasticLogo()).elasticLogo;
});
describe('default output', () => {
it('returns a containerStyle', () => {
const result = fn(null);

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { getElasticLogo, resolveFromArgs } from '@kbn/presentation-util-plugin/common';
import { elasticLogo, resolveFromArgs } from '@kbn/expression-utils';
import { ViewStrings } from '../../../i18n';
const { Image: strings } = ViewStrings;
@ -21,7 +21,6 @@ export const image = () => {
name: 'dataurl',
argType: 'imageUpload',
resolve: async ({ args }) => {
const { elasticLogo } = await getElasticLogo();
return { dataurl: resolveFromArgs(args, elasticLogo) };
},
},

View file

@ -8,7 +8,7 @@
import { Dispatch } from 'redux';
import { connect } from 'react-redux';
import { get } from 'lodash';
import { encode } from '@kbn/presentation-util-plugin/common';
import { encode } from '@kbn/expression-utils';
// @ts-expect-error untyped local
import { findExistingAsset } from '../../lib/find_existing_asset';
import { VALID_IMAGE_TYPES } from '../../../common/lib/constants';

View file

@ -8,8 +8,7 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { waitFor } from '@kbn/presentation-util-plugin/public/__stories__';
import { getElasticLogo } from '@kbn/presentation-util-plugin/common';
import { elasticLogo } from '@kbn/expression-utils';
import { CustomElementModal } from '../custom_element_modal';
storiesOf('components/Elements/CustomElementModal', module)
@ -42,10 +41,10 @@ storiesOf('components/Elements/CustomElementModal', module)
(_, props) => (
<CustomElementModal
title="Edit custom element"
image={props?.elasticLogo}
image={elasticLogo}
onCancel={action('onCancel')}
onSave={action('onSave')}
/>
),
{ decorators: [waitFor(getElasticLogo())] }
{ decorators: [] }
);

View file

@ -28,7 +28,7 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { encode } from '@kbn/presentation-util-plugin/common';
import { encode } from '@kbn/expression-utils';
import { VALID_IMAGE_TYPES } from '../../../common/lib/constants';
import { ElementCard } from '../element_card';
const MAX_NAME_LENGTH = 40;

View file

@ -9,7 +9,7 @@ import { toByteArray } from 'base64-js';
import fileSaver from 'file-saver';
import PropTypes from 'prop-types';
import React, { ReactElement } from 'react';
import { parseDataUrl } from '@kbn/presentation-util-plugin/common';
import { parseDataUrl } from '@kbn/expression-utils';
interface Props {
children: ReactElement<any>;

View file

@ -8,8 +8,7 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { waitFor } from '@kbn/presentation-util-plugin/public/__stories__';
import { getElasticLogo } from '@kbn/presentation-util-plugin/common';
import { elasticLogo } from '@kbn/expression-utils';
import { ElementCard } from '../element_card';
storiesOf('components/Elements/ElementCard', module)
@ -34,10 +33,10 @@ storiesOf('components/Elements/ElementCard', module)
<ElementCard
title="Element 1"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce lobortis aliquet arcu ut turpis duis."
image={props?.elasticLogo}
image={elasticLogo}
/>
),
{ decorators: [waitFor(getElasticLogo())] }
{ decorators: [] }
)
.add('with tags', () => (
<ElementCard

View file

@ -5,10 +5,9 @@
* 2.0.
*/
import { getElasticLogo } from '@kbn/presentation-util-plugin/common';
import { elasticLogo } from '@kbn/expression-utils';
export const getTestCustomElements = async () => {
const { elasticLogo } = await getElasticLogo();
const testCustomElements = [
{
id: 'custom-element-10d625f5-1342-47c9-8f19-d174ea6b65d5',

View file

@ -88,7 +88,8 @@
"@kbn/presentation-publishing",
"@kbn/react-kibana-context-render",
"@kbn/search-types",
"@kbn/visualizations-plugin"
"@kbn/visualizations-plugin",
"@kbn/expression-utils"
],
"exclude": ["target/**/*"]
}

View file

@ -5725,6 +5725,10 @@
version "0.0.0"
uid ""
"@kbn/expression-utils@link:src/platform/packages/private/kbn-expression-utils":
version "0.0.0"
uid ""
"@kbn/expression-xy-plugin@link:src/platform/plugins/shared/chart_expressions/expression_xy":
version "0.0.0"
uid ""