diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 8121ca6706baf..b17a6f0581daa 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -723,6 +723,7 @@ test/plugin_functional/plugins/session_notifications @elastic/kibana-core
x-pack/plugins/session_view @elastic/kibana-cloud-security-posture
packages/kbn-set-map @elastic/kibana-operations
examples/share_examples @elastic/appex-sharedux
+packages/kbn-share-modal @elastic/appex-sharedux
src/plugins/share @elastic/appex-sharedux
packages/kbn-shared-svg @elastic/obs-ux-infra_services-team
packages/shared-ux/avatar/solution @elastic/appex-sharedux
diff --git a/package.json b/package.json
index 2aca3ef5b12fb..2a7bc713226ec 100644
--- a/package.json
+++ b/package.json
@@ -775,6 +775,7 @@
"@kbn/shared-ux-router-types": "link:packages/shared-ux/router/types",
"@kbn/shared-ux-storybook-config": "link:packages/shared-ux/storybook/config",
"@kbn/shared-ux-storybook-mock": "link:packages/shared-ux/storybook/mock",
+ "@kbn/shared-ux-tabbed-modal": "link:packages/shared-ux/modal/tabbed",
"@kbn/shared-ux-utility": "link:packages/kbn-shared-ux-utility",
"@kbn/slo-schema": "link:x-pack/packages/kbn-slo-schema",
"@kbn/snapshot-restore-plugin": "link:x-pack/plugins/snapshot_restore",
diff --git a/packages/kbn-share-modal/ tsconfig.json b/packages/kbn-share-modal/tsconfig.json
similarity index 100%
rename from packages/kbn-share-modal/ tsconfig.json
rename to packages/kbn-share-modal/tsconfig.json
diff --git a/packages/shared-ux/modal/tabbed/README.mdx b/packages/shared-ux/modal/tabbed/README.mdx
new file mode 100644
index 0000000000000..827fff0e9135e
--- /dev/null
+++ b/packages/shared-ux/modal/tabbed/README.mdx
@@ -0,0 +1,79 @@
+---
+id: sharedUX/Components/KibanaTabbedModal
+slug: /shared-ux/components/tabbed-modal
+title: Tabbed Modal
+description: A wrapper around `EuiAvatar` tailored for use in Kibana solutions.
+tags: ['shared-ux', 'component']
+date: 2024-03-07
+---
+
+## Description
+
+A wrapper around `EuiModal` for displaying and managing tabs tailored for use in Kibana solutions.
+
+## Usage
+
+A fairly trival use case might look like this
+
+```tsx
+const hello: IModalTabDeclaration<{ message: string }> = {
+ id: 'hello',
+ title: 'Say Hello',
+ initialState: {
+ message: 'Hello World!',
+ },
+ content: ({ state }) => {
+ // we can access state here to impact renders
+ return
Click the button to trigger a message
;
+ },
+ modalActionBtn: {
+ label: 'Click me!',
+ handler: ({ state }) => {
+ // state can also be accessed here if neccessary
+ alert(state.message);
+ },
+ },
+};
+
+
+```
+
+a slightly more complex use case, where we'd like changes from interactions within the rendered tab
+content to propagate to the state might look similar to this.
+
+```tsx
+const hello: IModalTabDeclaration<{ message: string }> = {
+ id: 'hello',
+ title: 'Say Hello',
+ initialState: {
+ message: 'Hello World!',
+ },
+ reducer(state, action) {
+ switch(action.type) {
+ case 'CHANGE_MESSAGE':
+ return {
+ ...state,
+ message: action.payload
+ };
+ default:
+ return State;
+ }
+ },
+ content: ({ dispatch }) => {
+ useEffect(() => {
+ dispatch({ type: 'CHANGE_MESSAGE', payload: 'Hello from the other side!' });
+ }, []);
+ // we can access state here to impact renders
+ return Click the button to trigger a message
;
+ },
+ modalActionBtn: {
+ label: 'Click me!',
+ handler: ({ state }) => {
+ // message displayed in alert here would be different from the value set in our initial state
+ alert(state.message);
+ },
+ },
+};
+
+
+```
diff --git a/packages/shared-ux/share_modal/impl/src/share_modal.stories.tsx b/packages/shared-ux/modal/tabbed/index.tsx
similarity index 76%
rename from packages/shared-ux/share_modal/impl/src/share_modal.stories.tsx
rename to packages/shared-ux/modal/tabbed/index.tsx
index 5c2d5b68ae2e0..574cd435f68ed 100644
--- a/packages/shared-ux/share_modal/impl/src/share_modal.stories.tsx
+++ b/packages/shared-ux/modal/tabbed/index.tsx
@@ -5,3 +5,6 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
+
+export { TabbedModal } from './src/tabbed_modal';
+export type { IModalTabDeclaration } from './src/context';
diff --git a/packages/shared-ux/modal/tabbed/kibana.jsonc b/packages/shared-ux/modal/tabbed/kibana.jsonc
new file mode 100644
index 0000000000000..4d6f0d69600c0
--- /dev/null
+++ b/packages/shared-ux/modal/tabbed/kibana.jsonc
@@ -0,0 +1,6 @@
+{
+ "type": "shared-common",
+ "id": "@kbn/shared-ux-tabbed-modal",
+ "owner": "@elastic/appex-sharedux"
+ }
+
\ No newline at end of file
diff --git a/packages/shared-ux/modal/tabbed/package.json b/packages/shared-ux/modal/tabbed/package.json
new file mode 100644
index 0000000000000..73c69504f0c62
--- /dev/null
+++ b/packages/shared-ux/modal/tabbed/package.json
@@ -0,0 +1,6 @@
+{
+ "name": "@kbn/shared-ux-tabbed-modal",
+ "private": true,
+ "version": "1.0.0",
+ "license": "SSPL-1.0 OR Elastic License 2.0"
+}
diff --git a/packages/shared-ux/modal/tabbed/src/context/index.tsx b/packages/shared-ux/modal/tabbed/src/context/index.tsx
new file mode 100644
index 0000000000000..202a81123c3ba
--- /dev/null
+++ b/packages/shared-ux/modal/tabbed/src/context/index.tsx
@@ -0,0 +1,152 @@
+/*
+ * 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 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 or the Server
+ * Side Public License, v 1.
+ */
+
+import React, {
+ createContext,
+ useContext,
+ useReducer,
+ useMemo,
+ useRef,
+ useCallback,
+ type PropsWithChildren,
+ type ReactElement,
+ type Dispatch,
+} from 'react';
+import { type EuiTabProps, type CommonProps } from '@elastic/eui';
+
+interface IDispatchAction {
+ type: string;
+ payload: any;
+}
+
+export type IModalTabState = Record;
+
+// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
+type IModalMetaState = {
+ selectedTabId: string | null;
+};
+
+type IReducer = (state: S, action: IDispatchAction) => S;
+
+export type IModalTabContent = (props: {
+ state: S;
+ dispatch: Dispatch;
+}) => ReactElement;
+
+interface IModalTabActionBtn extends CommonProps {
+ label: string;
+ handler: (args: { state: S }) => void;
+}
+
+export interface IModalTabDeclaration extends EuiTabProps {
+ id: string;
+ title: string;
+ initialState?: Partial;
+ reducer?: IReducer;
+ content: IModalTabContent;
+ modalActionBtn: IModalTabActionBtn;
+}
+
+interface IModalContext {
+ tabs: Array, 'reducer' | 'initialState'>>;
+ state: { meta: IModalMetaState } & Record;
+ dispatch: Dispatch;
+}
+
+const ModalContext = createContext({
+ tabs: [],
+ state: {
+ meta: {
+ selectedTabId: null,
+ },
+ },
+ dispatch: () => {},
+});
+
+/**
+ * @description defines state transition for meta information to manage the modal, meta action types
+ * must be prefixed with the string 'META_'
+ */
+const modalMetaReducer: IReducer = (state, action) => {
+ switch (action.type) {
+ case 'META_selectedTabId':
+ return {
+ ...state,
+ selectedTabId: action.payload as string,
+ };
+ default:
+ return state;
+ }
+};
+
+export type IModalContextProviderProps>> =
+ PropsWithChildren<{
+ tabs: Tabs;
+ selectedTabId: Tabs[number]['id'];
+ }>;
+
+export function ModalContextProvider>>({
+ tabs,
+ selectedTabId,
+ children,
+}: IModalContextProviderProps) {
+ const modalTabDefinitions = useRef([]);
+
+ const initialModalState = useRef({
+ // instantiate state with default meta information
+ meta: {
+ selectedTabId,
+ },
+ });
+
+ const reducersMap = useMemo(
+ () =>
+ tabs.reduce((result, { id, reducer, initialState, ...rest }) => {
+ initialModalState.current[id] = initialState ?? {};
+ modalTabDefinitions.current.push({ id, ...rest });
+ result[id] = reducer;
+ return result;
+ }, {}),
+ [tabs]
+ );
+
+ const combineReducers = useCallback(
+ function (reducers: Record>) {
+ return (state: IModalContext['state'], action: IDispatchAction) => {
+ const newState = { ...state };
+
+ if (/^meta_/i.test(action.type)) {
+ newState.meta = modalMetaReducer(newState.meta, action);
+ } else {
+ newState[selectedTabId] = reducers[selectedTabId](newState[selectedTabId], action);
+ }
+
+ return newState;
+ };
+ },
+ [selectedTabId]
+ );
+
+ const createInitialState = useCallback((state: IModalContext['state']) => {
+ return state;
+ }, []);
+
+ const [state, dispatch] = useReducer(
+ combineReducers(reducersMap),
+ initialModalState.current,
+ createInitialState
+ );
+
+ return (
+
+ {children}
+
+ );
+}
+
+export const useModalContext = () => useContext(ModalContext);
diff --git a/packages/shared-ux/modal/tabbed/src/storybook/setup.ts b/packages/shared-ux/modal/tabbed/src/storybook/setup.ts
new file mode 100644
index 0000000000000..04f50dc9345bb
--- /dev/null
+++ b/packages/shared-ux/modal/tabbed/src/storybook/setup.ts
@@ -0,0 +1,52 @@
+/*
+ * 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 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 or the Server
+ * Side Public License, v 1.
+ */
+
+import type { ComponentProps } from 'react';
+import { AbstractStorybookMock } from '@kbn/shared-ux-storybook-mock';
+
+import TabbedModal from '../..';
+
+type TabbedModalProps = ComponentProps;
+type TabbedModalServiceArguments = Record;
+
+type Arguments = TabbedModalProps & TabbedModalServiceArguments;
+
+/**
+ * Storybook parameters provided from the controls addon.
+ */
+export type Params = Record;
+
+export class StorybookMock extends AbstractStorybookMock<
+ TabbedModalProps,
+ TabbedModalServiceArguments,
+ TabbedModalProps,
+ TabbedModalServiceArguments
+> {
+ propArguments = {
+ tabs: {
+ control: {
+ type: 'array',
+ },
+ defaultValue: [],
+ },
+ };
+
+ serviceArguments = {};
+
+ dependencies = [];
+
+ getProps(params?: Params): TabbedModalProps {
+ return {
+ tabs: this.getArgumentValue('tabs', params),
+ };
+ }
+
+ getServices(params: Params): TabbedModalServiceArguments {
+ return {};
+ }
+}
diff --git a/packages/shared-ux/modal/tabbed/src/tabbed_modal.stories.tsx b/packages/shared-ux/modal/tabbed/src/tabbed_modal.stories.tsx
new file mode 100644
index 0000000000000..8f81acab27303
--- /dev/null
+++ b/packages/shared-ux/modal/tabbed/src/tabbed_modal.stories.tsx
@@ -0,0 +1,175 @@
+/*
+ * 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 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 or the Server
+ * Side Public License, v 1.
+ */
+
+import { EuiText, EuiCheckboxGroup, EuiSpacer, useGeneratedHtmlId } from '@elastic/eui';
+import React, { Fragment } from 'react';
+
+import {
+ StorybookMock as TabbedModalStorybookMock,
+ type Params as TabbedModalStorybookParams,
+} from './storybook/setup';
+
+import { TabbedModal } from './tabbed_modal';
+import { IModalTabDeclaration } from './context';
+
+export default {
+ title: 'Modal/Tabbed Modal',
+ description: 'A controlled modal component that renders tabs',
+};
+
+const mock = new TabbedModalStorybookMock();
+const argTypes = mock.getArgumentTypes();
+
+export const TrivialExample = (params: TabbedModalStorybookParams) => {
+ return (
+ {
+ return (
+
+
+
+ Click the button to send a message into the void
+
+
+ );
+ },
+ initialState: {
+ message: 'Hello World!!',
+ },
+ modalActionBtn: {
+ label: 'Say Hi 👋🏾',
+ handler: ({ state }) => {
+ alert(state.message);
+ },
+ },
+ },
+ ]}
+ selectedTabId="hello"
+ onClose={() => {}}
+ />
+ );
+};
+
+TrivialExample.argTypes = argTypes;
+
+export const NonTrivialExample = (params: TabbedModalStorybookParams) => {
+ const checkboxGroupItemId1 = useGeneratedHtmlId({
+ prefix: 'checkboxGroupItem',
+ suffix: 'first',
+ });
+ const checkboxGroupItemId2 = useGeneratedHtmlId({
+ prefix: 'checkboxGroupItem',
+ suffix: 'second',
+ });
+ const checkboxGroupItemId3 = useGeneratedHtmlId({
+ prefix: 'checkboxGroupItem',
+ suffix: 'third',
+ });
+
+ const checkboxes = [
+ {
+ id: checkboxGroupItemId1,
+ label: 'Margherita',
+ 'data-test-sub': 'dts_test',
+ },
+ {
+ id: checkboxGroupItemId2,
+ label: 'Diavola',
+ className: 'classNameTest',
+ },
+ {
+ id: checkboxGroupItemId3,
+ label: 'Hawaiian Pizza',
+ disabled: true,
+ },
+ ];
+
+ enum ACTION_TYPES {
+ SelectOption,
+ }
+
+ const pizzaSelector: IModalTabDeclaration<{
+ checkboxIdToSelectedMap: Record;
+ }> = {
+ id: 'order',
+ title: 'Pizza of choice',
+ initialState: {
+ checkboxIdToSelectedMap: {
+ [checkboxGroupItemId2]: true,
+ },
+ },
+ reducer(state, action) {
+ switch (action.type) {
+ case String(ACTION_TYPES.SelectOption):
+ return {
+ ...state,
+ checkboxIdToSelectedMap: action.payload,
+ };
+ default:
+ return state;
+ }
+ },
+ content: ({ state, dispatch }) => {
+ const { checkboxIdToSelectedMap } = state;
+
+ const onChange = (optionId) => {
+ const newCheckboxIdToSelectedMap = {
+ ...checkboxIdToSelectedMap,
+ ...{
+ [optionId]: !checkboxIdToSelectedMap[optionId],
+ },
+ };
+
+ dispatch({
+ type: String(ACTION_TYPES.SelectOption),
+ payload: newCheckboxIdToSelectedMap,
+ });
+ };
+
+ return (
+
+
+
+ Select a Pizza (or more)
+
+
+ onChange(id)}
+ />
+
+ );
+ },
+ modalActionBtn: {
+ label: 'Order 🍕',
+ handler: ({ state }) => {
+ alert(JSON.stringify(state));
+ },
+ },
+ };
+
+ // TODO: fix type mismatch
+ return (
+ {}}
+ modalTitle="Non trivial example"
+ tabs={[pizzaSelector]}
+ selectedTabId="order"
+ />
+ );
+};
+
+NonTrivialExample.argTypes = argTypes;
diff --git a/packages/shared-ux/modal/tabbed/src/tabbed_modal.tsx b/packages/shared-ux/modal/tabbed/src/tabbed_modal.tsx
new file mode 100644
index 0000000000000..b87c80fb53bd1
--- /dev/null
+++ b/packages/shared-ux/modal/tabbed/src/tabbed_modal.tsx
@@ -0,0 +1,106 @@
+/*
+ * 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 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 or the Server
+ * Side Public License, v 1.
+ */
+
+import React, { useMemo, Fragment, type ComponentProps, type FC, useCallback } from 'react';
+import {
+ EuiButton,
+ EuiModal,
+ EuiModalBody,
+ EuiModalFooter,
+ EuiModalHeader,
+ EuiModalHeaderTitle,
+ EuiTabs,
+ EuiTab,
+} from '@elastic/eui';
+import {
+ ModalContextProvider,
+ useModalContext,
+ type IModalTabState,
+ type IModalTabDeclaration,
+ type IModalContextProviderProps,
+} from './context';
+
+interface ITabbedModalInner extends Pick, 'onClose'> {
+ modalTitle?: string;
+}
+
+const TabbedModalInner: FC = ({ onClose, modalTitle }) => {
+ const { tabs, state, dispatch } = useModalContext();
+
+ const selectedTabId = state.meta.selectedTabId;
+ const selectedTabState = useMemo(
+ () => (selectedTabId ? state[selectedTabId] : {}),
+ [selectedTabId, state]
+ );
+
+ const {
+ content: SelectedTabContent,
+ modalActionBtn: { label, handler },
+ } = useMemo(() => {
+ return tabs.find((obj) => obj.id === selectedTabId)!;
+ }, [selectedTabId, tabs]);
+
+ const onSelectedTabChanged = (id: string) => {
+ dispatch({ type: 'META_selectedTabId', payload: id });
+ };
+
+ const renderTabs = () => {
+ return tabs.map((tab, index) => (
+ onSelectedTabChanged(tab.id)}
+ isSelected={tab.id === selectedTabId}
+ disabled={tab.disabled}
+ prepend={tab.prepend}
+ append={tab.append}
+ >
+ {tab.title}
+
+ ));
+ };
+
+ const btnClickHandler = useCallback(() => {
+ handler({ state: selectedTabState });
+ }, [handler, selectedTabState]);
+
+ return (
+
+ {Boolean(modalTitle) ? (
+
+ {modalTitle}
+
+ ) : null}
+
+
+ {renderTabs()}
+ {React.createElement(SelectedTabContent, {
+ state: selectedTabState,
+ dispatch,
+ })}
+
+
+
+
+ {label}
+
+
+
+ );
+};
+
+export function TabbedModal>>({
+ tabs,
+ selectedTabId,
+ ...rest
+}: Omit, 'children'> & ITabbedModalInner) {
+ return (
+
+
+
+ );
+}
diff --git a/packages/shared-ux/share_modal/mocks/index.ts b/packages/shared-ux/share_modal/mocks/index.ts
deleted file mode 100644
index dd836d1d0ed57..0000000000000
--- a/packages/shared-ux/share_modal/mocks/index.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * 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 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 or the Server
- * Side Public License, v 1.
- */
-
-export { StorybookMock as ShareModalStorybookMock } from './src/storybook';
-export type { Params as ShareModalStorybookParams } from './src/storybook';
diff --git a/packages/shared-ux/share_modal/mocks/src/storybook.ts b/packages/shared-ux/share_modal/mocks/src/storybook.ts
deleted file mode 100644
index 489bc8e3d10e7..0000000000000
--- a/packages/shared-ux/share_modal/mocks/src/storybook.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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 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 or the Server
- * Side Public License, v 1.
- */
-
-import { AbstractStorybookMock, ArgumentParams } from '@kbn/shared-ux-storybook-mock';
-import { ModalProps as ShareModalProps } from '@kbn/share-modal';
-
-import { ArgTypes } from '@storybook/react';
-import { ShareModalStorybookMock } from '..';
-
-type PropArguments = Pick;
-type Arguments = PropArguments;
-
-/**
- * Storybook parameters provided from the controls addon.
- */
-export type Params = Record;
-
-const redirectMock = new ShareModalStorybookMock();
-
-export class StorybookMock extends AbstractStorybookMock {
- serviceArguments: ArgTypes<{}>;
- getServices(params?: ArgumentParams | undefined): {} {
- throw new Error('Method not implemented.');
- }
- propArguments = {
- objectType: {
- control: {
- type: 'text',
- },
- defaultValue: '',
- },
- modalBodyDescriptions: {
- control: {
- type: 'text',
- },
- defaultValue: '',
- },
- tabs: {},
- };
-
- dependencies = [];
-
- getProps(params?: Params): ShareModalProps {
- return {
- modalBodyDescriptions: this.getArgumentValue('description', params),
- objectType: this.getArgumentValue('objectType', params),
- tabs: this.getArgumentValue('tabs', params),
- };
- }
-}
diff --git a/packages/shared-ux/share_modal/types/index.d.ts b/packages/shared-ux/share_modal/types/index.d.ts
deleted file mode 100644
index 427989f9e90c4..0000000000000
--- a/packages/shared-ux/share_modal/types/index.d.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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 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 or the Server
- * Side Public License, v 1.
- */
-
-import { ModalProps } from '@kbn/share-modal';
-import { ReactElement } from 'react';
-
-/**
- * Props for the `ShareModal` pure component.
- */
-export type ShareModalComponentProps = Partial<
- Pick
-> & {
- objectType: string;
- modalBodyDescription: string;
- tabs: Array<{ id: string; name: string; content: ReactElement }>;
-};
-
-export type ShareModalProps = ShareModalComponentProps;
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 8ee230dfa6dba..79f6bbb627c3f 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -1540,6 +1540,8 @@
"@kbn/shared-ux-storybook-config/*": ["packages/shared-ux/storybook/config/*"],
"@kbn/shared-ux-storybook-mock": ["packages/shared-ux/storybook/mock"],
"@kbn/shared-ux-storybook-mock/*": ["packages/shared-ux/storybook/mock/*"],
+ "@kbn/shared-ux-tabbed-modal": ["packages/shared-ux/modal/tabbed"],
+ "@kbn/shared-ux-tabbed-modal/*": ["packages/shared-ux/modal/tabbed/*"],
"@kbn/shared-ux-utility": ["packages/kbn-shared-ux-utility"],
"@kbn/shared-ux-utility/*": ["packages/kbn-shared-ux-utility/*"],
"@kbn/slo-schema": ["x-pack/packages/kbn-slo-schema"],
diff --git a/yarn.lock b/yarn.lock
index 36a3408b03a1a..83d191f0b0fbb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6148,6 +6148,10 @@
version "0.0.0"
uid ""
+"@kbn/shared-ux-tabbed-modal@link:packages/shared-ux/modal/tabbed":
+ version "0.0.0"
+ uid ""
+
"@kbn/shared-ux-utility@link:packages/kbn-shared-ux-utility":
version "0.0.0"
uid ""