) => {
return services;
};
+const theme$ = of({ darkMode: false });
+
export function WithServices(Comp: ComponentType
, overrides: Partial = {}) {
return (props: P) => {
const services = getMockServices(overrides);
return (
- undefined}>
+ undefined} theme$={theme$}>
diff --git a/packages/content-management/table_list_view_table/src/services.tsx b/packages/content-management/table_list_view_table/src/services.tsx
index d7752a270c86c..5ee9f9d4b32bd 100644
--- a/packages/content-management/table_list_view_table/src/services.tsx
+++ b/packages/content-management/table_list_view_table/src/services.tsx
@@ -99,6 +99,11 @@ export interface TableListViewKibanaDependencies {
overlays: {
openFlyout(mount: MountPoint, options?: OverlayFlyoutOpenOptions): OverlayRef;
};
+ theme: {
+ theme$: Observable<{
+ readonly darkMode: boolean;
+ }>;
+ };
};
/**
* Handler from the '@kbn/kibana-react-plugin/public' Plugin
diff --git a/packages/core/application/core-application-common/src/default_app_categories.ts b/packages/core/application/core-application-common/src/default_app_categories.ts
index 708b5cb457ab1..bf3f02d26c23d 100644
--- a/packages/core/application/core-application-common/src/default_app_categories.ts
+++ b/packages/core/application/core-application-common/src/default_app_categories.ts
@@ -21,8 +21,8 @@ export const DEFAULT_APP_CATEGORIES: Record = Object.freeze
},
enterpriseSearch: {
id: 'enterpriseSearch',
- label: i18n.translate('core.ui.enterpriseSearchNavList.label', {
- defaultMessage: 'Enterprise Search',
+ label: i18n.translate('core.ui.searchNavList.label', {
+ defaultMessage: 'Search',
}),
order: 2000,
euiIconType: 'logoEnterpriseSearch',
diff --git a/packages/core/http/core-http-router-server-internal/index.ts b/packages/core/http/core-http-router-server-internal/index.ts
index 026c6b854e8bb..1081af2c4aa70 100644
--- a/packages/core/http/core-http-router-server-internal/index.ts
+++ b/packages/core/http/core-http-router-server-internal/index.ts
@@ -8,6 +8,7 @@
export { filterHeaders } from './src/headers';
export { Router, type RouterOptions } from './src/router';
+export type { HandlerResolutionStrategy } from './src/versioned_router';
export { isKibanaRequest, isRealRequest, ensureRawRequest, CoreKibanaRequest } from './src/request';
export { isSafeMethod } from './src/route';
export { HapiResponseAdapter } from './src/response_adapter';
diff --git a/packages/core/http/core-http-router-server-internal/src/router.ts b/packages/core/http/core-http-router-server-internal/src/router.ts
index 509f407ff401a..617c810109138 100644
--- a/packages/core/http/core-http-router-server-internal/src/router.ts
+++ b/packages/core/http/core-http-router-server-internal/src/router.ts
@@ -124,10 +124,10 @@ export interface RouterOptions {
/** Whether we are running in development */
isDev?: boolean;
/**
- * Which route resolution algo to use
- * @default 'oldest'
+ * Which route resolution algo to use.
+ * @note default to "oldest", but when running in dev default to "none"
*/
- versionedRouteResolution?: 'newest' | 'oldest';
+ versionedRouteResolution?: 'newest' | 'oldest' | 'none';
}
/**
diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts
index 339890fbdddec..dd1a0c21d5593 100644
--- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts
+++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts
@@ -71,7 +71,6 @@ export class CoreVersionedRoute implements VersionedRoute {
}
private isPublic: boolean;
- private isInternal: boolean;
private enableQueryVersion: boolean;
private constructor(
private readonly router: CoreVersionedRouter,
@@ -81,7 +80,6 @@ export class CoreVersionedRoute implements VersionedRoute {
) {
this.isPublic = this.options.access === 'public';
this.enableQueryVersion = this.options.enableQueryVersion === true;
- this.isInternal = !this.isPublic;
this.router.router[this.method](
{
path: this.path,
@@ -100,7 +98,7 @@ export class CoreVersionedRoute implements VersionedRoute {
}
/** This method assumes that one or more versions handlers are registered */
- private getDefaultVersion(): ApiVersion {
+ private getDefaultVersion(): undefined | ApiVersion {
return resolvers[this.router.defaultHandlerResolutionStrategy]([...this.handlers.keys()]);
}
@@ -120,8 +118,15 @@ export class CoreVersionedRoute implements VersionedRoute {
});
}
const req = originalReq as Mutable;
- const requestVersion = readVersion(req, this.enableQueryVersion);
- if (!requestVersion && !this.canUseDefaultVersion()) {
+ let version: undefined | ApiVersion;
+
+ const maybeVersion = readVersion(req, this.enableQueryVersion);
+ if (!maybeVersion && this.isPublic) {
+ version = this.getDefaultVersion();
+ } else {
+ version = maybeVersion;
+ }
+ if (!version) {
return res.badRequest({
body: `Please specify a version via ${ELASTIC_HTTP_VERSION_HEADER} header. Available versions: ${this.versionsToString()}`,
});
@@ -135,7 +140,6 @@ export class CoreVersionedRoute implements VersionedRoute {
body: `Use of query parameter "${ELASTIC_HTTP_VERSION_QUERY_PARAM}" is not allowed. Please specify the API version using the "${ELASTIC_HTTP_VERSION_HEADER}" header.`,
});
}
- const version: ApiVersion = requestVersion ?? this.getDefaultVersion();
const invalidVersionMessage = isValidRouteVersion(this.isPublic, version);
if (invalidVersionMessage) {
@@ -198,10 +202,6 @@ export class CoreVersionedRoute implements VersionedRoute {
);
};
- private canUseDefaultVersion(): boolean {
- return !this.isInternal && !this.router.isDev;
- }
-
private validateVersion(version: string) {
// We do an additional check here while we only have a single allowed public version
// for all public Kibana HTTP APIs
diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/handler_resolvers.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/handler_resolvers.ts
index 376116c02607b..22f127ff71d95 100644
--- a/packages/core/http/core-http-router-server-internal/src/versioned_router/handler_resolvers.ts
+++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/handler_resolvers.ts
@@ -10,13 +10,16 @@
* Assumes that there is at least one version in the array.
* @internal
*/
-type Resolver = (versions: string[]) => string;
+type Resolver = (versions: string[]) => undefined | string;
const oldest: Resolver = (versions) => [...versions].sort((a, b) => a.localeCompare(b))[0];
const newest: Resolver = (versions) => [...versions].sort((a, b) => b.localeCompare(a))[0];
+const none: Resolver = () => undefined;
+
export const resolvers = {
oldest,
newest,
+ none,
};
diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/index.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/index.ts
index a7d6e9c82fdb6..941b6b5e5706f 100644
--- a/packages/core/http/core-http-router-server-internal/src/versioned_router/index.ts
+++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/index.ts
@@ -7,3 +7,4 @@
*/
export { CoreVersionedRouter } from './core_versioned_router';
+export type { HandlerResolutionStrategy } from './types';
diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts
index be0471c93848c..4004004036c53 100644
--- a/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts
+++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts
@@ -42,4 +42,6 @@ export type HandlerResolutionStrategy =
/** Use the oldest available version by default */
| 'oldest'
/** Use the newest available version by default */
- | 'newest';
+ | 'newest'
+ /** Dev-only: remove resolution and fail if no version is provided */
+ | 'none';
diff --git a/packages/core/http/core-http-server-internal/src/http_config.test.ts b/packages/core/http/core-http-server-internal/src/http_config.test.ts
index 9e9c7febed995..c535bc64fbb00 100644
--- a/packages/core/http/core-http-server-internal/src/http_config.test.ts
+++ b/packages/core/http/core-http-server-internal/src/http_config.test.ts
@@ -486,6 +486,29 @@ describe('cors', () => {
});
});
+describe('versioned', () => {
+ it('defaults version resolution "oldest" not in dev', () => {
+ expect(config.schema.validate({}, { dev: undefined })).toMatchObject({
+ versioned: { versionResolution: 'oldest' },
+ });
+ expect(config.schema.validate({}, { dev: false })).toMatchObject({
+ versioned: { versionResolution: 'oldest' },
+ });
+ });
+
+ it('does not allow "none" when not in dev', () => {
+ expect(() =>
+ config.schema.validate({ versioned: { versionResolution: 'none' } }, { dev: false })
+ ).toThrow(/failed validation/);
+ });
+
+ it('defaults version resolution "none" when in dev', () => {
+ expect(config.schema.validate({}, { dev: true })).toMatchObject({
+ versioned: { versionResolution: 'none' },
+ });
+ });
+});
+
describe('HttpConfig', () => {
it('converts customResponseHeaders to strings or arrays of strings', () => {
const httpSchema = config.schema;
diff --git a/packages/core/http/core-http-server-internal/src/http_config.ts b/packages/core/http/core-http-server-internal/src/http_config.ts
index 3a96b97b88cbf..d0a3ced0d5fab 100644
--- a/packages/core/http/core-http-server-internal/src/http_config.ts
+++ b/packages/core/http/core-http-server-internal/src/http_config.ts
@@ -17,6 +17,7 @@ import url from 'url';
import type { Duration } from 'moment';
import { IHttpEluMonitorConfig } from '@kbn/core-http-server/src/elu_monitor';
+import type { HandlerResolutionStrategy } from '@kbn/core-http-router-server-internal';
import { CspConfigType, CspConfig } from './csp';
import { ExternalUrlConfig } from './external_url';
import {
@@ -168,11 +169,30 @@ const configSchema = schema.object(
),
restrictInternalApis: schema.boolean({ defaultValue: false }), // allow access to internal routes by default to prevent breaking changes in current offerings
versioned: schema.object({
- // Which handler resolution algo to use: "newest" or "oldest"
- versionResolution: schema.oneOf([schema.literal('newest'), schema.literal('oldest')], {
- defaultValue: 'oldest',
- }),
- // Whether we enforce version checks on client requests
+ /**
+ * Which handler resolution algo to use: "newest" or "oldest".
+ *
+ * @note in development we have an additional option "none" which is also the default.
+ * This prevents any fallbacks and requires that a version specified.
+ * Useful for ensuring that a given client always specifies a version.
+ */
+ versionResolution: schema.conditional(
+ schema.contextRef('dev'),
+ true,
+ schema.oneOf([schema.literal('newest'), schema.literal('oldest'), schema.literal('none')], {
+ defaultValue: 'none',
+ }),
+ schema.oneOf([schema.literal('newest'), schema.literal('oldest')], {
+ defaultValue: 'oldest',
+ })
+ ),
+
+ /**
+ * Whether we require the Kibana browser build version to match the Kibana server build version.
+ *
+ * This number is determined when a distributable version of Kibana is built and ensures that only
+ * same-build browsers can access the Kibana server.
+ */
strictClientVersionCheck: schema.boolean({ defaultValue: true }),
}),
},
@@ -247,7 +267,10 @@ export class HttpConfig implements IHttpConfig {
public externalUrl: IExternalUrlConfig;
public xsrf: { disableProtection: boolean; allowlist: string[] };
public requestId: { allowFromAnyIp: boolean; ipAllowlist: string[] };
- public versioned: { versionResolution: 'newest' | 'oldest'; strictClientVersionCheck: boolean };
+ public versioned: {
+ versionResolution: HandlerResolutionStrategy;
+ strictClientVersionCheck: boolean;
+ };
public shutdownTimeout: Duration;
public restrictInternalApis: boolean;
diff --git a/packages/kbn-dom-drag-drop/README.md b/packages/kbn-dom-drag-drop/README.md
index 892bb60675d92..8415f6903eee8 100644
--- a/packages/kbn-dom-drag-drop/README.md
+++ b/packages/kbn-dom-drag-drop/README.md
@@ -9,7 +9,7 @@ We aren't using EUI or another library, due to the fact that Lens visualizations
First, place a RootDragDropProvider at the root of your application.
```js
-
+
... your app here ...
```
@@ -17,13 +17,13 @@ First, place a RootDragDropProvider at the root of your application.
If you have a child React application (e.g. a visualization), you will need to pass the drag / drop context down into it. This can be obtained like so:
```js
-const context = useContext(DragContext);
+const context = useDragDropContext();
```
-In your child application, place a `ChildDragDropProvider` at the root of that, and spread the context into it:
+In your child application, place a `ChildDragDropProvider` at the root of that, and assign the context into it:
```js
-... your child app here ...
+... your child app here ...
```
This enables your child application to share the same drag / drop context as the root application.
@@ -49,7 +49,7 @@ To enable dragging an item, use `DragDrop` with both a `draggable` and a `value`
To enable dropping, use `DragDrop` with both a `dropTypes` attribute that should be an array with at least one value and an `onDrop` handler attribute. `dropType` should only be truthy if is an item being dragged, and if a drop of the dragged item is supported.
```js
-const { dragging } = useContext(DragContext);
+const [ dndState ] = useDragDropContext()
return (
... elements from one group here ...
+... elements from one group here ...
```
The children `DragDrop` components must have props defined as in the example:
```js
-
+
{fields.map((f) => (
{
- const defaultContext = {
- dataTestSubjPrefix: 'testDragDrop',
+ const defaultContextState = {
dragging: undefined,
- setDragging: jest.fn(),
- setActiveDropTarget: jest.fn(),
+ dataTestSubjPrefix: 'testDragDrop',
activeDropTarget: undefined,
dropTargetsByOrder: undefined,
keyboardMode: false,
- setKeyboardMode: () => {},
- setA11yMessage: jest.fn(),
- registerDropTarget: jest.fn(),
};
const value = {
@@ -104,16 +97,9 @@ describe('DragDrop', () => {
});
test('dragstart sets dragging in the context and calls it with proper params', async () => {
- const setDragging = jest.fn();
-
- const setA11yMessage = jest.fn();
+ const dndDispatch = jest.fn();
const component = mount(
-
+
@@ -127,21 +113,24 @@ describe('DragDrop', () => {
});
expect(dataTransfer.setData).toBeCalledWith('text', 'hello');
- expect(setDragging).toBeCalledWith({ ...value });
- expect(setA11yMessage).toBeCalledWith('Lifted hello');
+ expect(dndDispatch).toBeCalledWith({
+ type: 'startDragging',
+ payload: { dragging: value },
+ });
});
test('drop resets all the things', async () => {
const preventDefault = jest.fn();
const stopPropagation = jest.fn();
- const setDragging = jest.fn();
+ const dndDispatch = jest.fn();
const onDrop = jest.fn();
const component = mount(
@@ -151,25 +140,27 @@ describe('DragDrop', () => {
const dragDrop = component.find('[data-test-subj="testDragDrop"]').at(0);
dragDrop.simulate('dragOver');
+ dndDispatch.mockClear();
dragDrop.simulate('drop', { preventDefault, stopPropagation });
expect(preventDefault).toBeCalled();
expect(stopPropagation).toBeCalled();
- expect(setDragging).toBeCalledWith(undefined);
expect(onDrop).toBeCalledWith({ id: '2', humanData: { label: 'Label1' } }, 'field_add');
+ expect(dndDispatch).toBeCalledWith({ type: 'resetState' });
});
test('drop function is not called on dropTypes undefined', async () => {
const preventDefault = jest.fn();
const stopPropagation = jest.fn();
- const setDragging = jest.fn();
+ const dndDispatch = jest.fn();
const onDrop = jest.fn();
const component = mount(
@@ -183,7 +174,7 @@ describe('DragDrop', () => {
expect(preventDefault).not.toHaveBeenCalled();
expect(stopPropagation).not.toHaveBeenCalled();
- expect(setDragging).not.toHaveBeenCalled();
+ expect(dndDispatch).not.toHaveBeenCalled();
expect(onDrop).not.toHaveBeenCalled();
});
@@ -206,7 +197,7 @@ describe('DragDrop', () => {
test('items that has dropTypes=undefined get special styling when another item is dragged', () => {
const component = mount(
-
+
@@ -228,17 +219,10 @@ describe('DragDrop', () => {
let dragging: { id: '1'; humanData: { label: 'Label1' } } | undefined;
const getAdditionalClassesOnEnter = jest.fn().mockReturnValue('additional');
const getAdditionalClassesOnDroppable = jest.fn().mockReturnValue('droppable');
- const setA11yMessage = jest.fn();
+ const dndDispatch = jest.fn();
const component = mount(
- {
- dragging = { id: '1', humanData: { label: 'Label1' } };
- }}
- >
+
{
act(() => {
jest.runAllTimers();
});
- expect(setA11yMessage).toBeCalledWith('Lifted ignored');
const dragDrop = component.find('[data-test-subj="testDragDrop"]').at(1);
dragDrop.simulate('dragOver');
@@ -278,22 +261,20 @@ describe('DragDrop', () => {
let dragging: { id: '1'; humanData: { label: 'Label1' } } | undefined;
const getAdditionalClasses = jest.fn().mockReturnValue('additional');
const getAdditionalClassesOnDroppable = jest.fn().mockReturnValue('droppable');
- const setActiveDropTarget = jest.fn();
+ const dndDispatch = jest.fn(() => {
+ dragging = { id: '1', humanData: { label: 'Label1' } };
+ });
const component = mount(
{
- dragging = { id: '1', humanData: { label: 'Label1' } };
- }}
- setActiveDropTarget={setActiveDropTarget}
- activeDropTarget={value as DragContextState['activeDropTarget']}
- keyboardMode={false}
- setKeyboardMode={(keyboardMode) => true}
- dropTargetsByOrder={undefined}
- registerDropTarget={jest.fn()}
+ value={[
+ {
+ ...defaultContextState,
+ dragging,
+ activeDropTarget: value as DragContextState['activeDropTarget'],
+ },
+ dndDispatch,
+ ]}
>
{
component.find('[data-test-subj="testDragDrop"]').at(1).simulate('dragover');
expect(component.find('.additional')).toHaveLength(2);
component.find('[data-test-subj="testDragDrop"]').at(1).simulate('dragleave');
- expect(setActiveDropTarget).toBeCalledWith(undefined);
+ expect(dndDispatch).toBeCalledWith({ type: 'leaveDropTarget' });
});
describe('Keyboard navigation', () => {
test('User receives proper drop Targets highlighted when pressing arrow keys', () => {
const onDrop = jest.fn();
- const setActiveDropTarget = jest.fn();
- const setA11yMessage = jest.fn();
+ const dndDispatch = jest.fn();
const items = [
{
draggable: true,
@@ -391,20 +371,21 @@ describe('DragDrop', () => {
];
const component = mount(
, style: {} } },
- setActiveDropTarget,
- setA11yMessage,
- activeDropTarget: { ...items[1].value, onDrop, dropType: 'move_compatible' },
- dropTargetsByOrder: {
- '2,0,1,0': { ...items[1].value, onDrop, dropType: 'move_compatible' },
- '2,0,2,0,0': { ...items[2].value, onDrop, dropType: 'replace_compatible' },
- '2,0,1,0,1': { ...items[1].value, onDrop, dropType: 'duplicate_compatible' },
- '2,0,1,0,2': { ...items[1].value, onDrop, dropType: 'swap_compatible' },
+ value={[
+ {
+ ...defaultContextState,
+ dragging: { ...items[0].value, ghost: { children: , style: {} } },
+ activeDropTarget: { ...items[1].value, onDrop, dropType: 'move_compatible' },
+ dropTargetsByOrder: {
+ '2,0,1,0': { ...items[1].value, onDrop, dropType: 'move_compatible' },
+ '2,0,2,0,0': { ...items[2].value, onDrop, dropType: 'replace_compatible' },
+ '2,0,1,0,1': { ...items[1].value, onDrop, dropType: 'duplicate_compatible' },
+ '2,0,1,0,2': { ...items[1].value, onDrop, dropType: 'swap_compatible' },
+ },
+ keyboardMode: true,
},
- keyboardMode: true,
- }}
+ dndDispatch,
+ ]}
>
{items.map((props) => (
@@ -418,17 +399,28 @@ describe('DragDrop', () => {
.at(1)
.simulate('focus');
+ dndDispatch.mockClear();
+
keyboardHandler.simulate('keydown', { key: 'ArrowRight' });
- expect(setActiveDropTarget).toBeCalledWith({
- ...items[2].value,
- onDrop,
- dropType: items[2].dropTypes![0],
+ expect(dndDispatch).toBeCalledWith({
+ type: 'selectDropTarget',
+ payload: {
+ dragging: items[0].value,
+ dropTarget: {
+ ...items[2].value,
+ onDrop,
+ dropType: items[2].dropTypes![0],
+ },
+ },
});
+
+ dndDispatch.mockClear();
+
keyboardHandler.simulate('keydown', { key: 'Enter' });
- expect(setA11yMessage).toBeCalledWith(
- `You're dragging Label1 from at position 1 in layer 0 over label3 from Y group at position 1 in layer 0. Press space or enter to replace label3 with Label1. Hold alt or option to duplicate. Hold shift to swap.`
- );
- expect(setActiveDropTarget).toBeCalledWith(undefined);
+ expect(dndDispatch).toBeCalledWith({
+ type: 'endDragging',
+ payload: { dragging: items[0].value },
+ });
expect(onDrop).toBeCalledWith(
{ humanData: { label: 'Label1', position: 1, layerNumber: 0 }, id: '1' },
'move_compatible'
@@ -436,16 +428,17 @@ describe('DragDrop', () => {
});
test('dragstart sets dragging in the context and calls it with proper params', async () => {
- const setDragging = jest.fn();
-
- const setA11yMessage = jest.fn();
+ const dndDispatch = jest.fn();
const component = mount(
@@ -463,23 +456,26 @@ describe('DragDrop', () => {
jest.runAllTimers();
});
- expect(setDragging).toBeCalledWith({
- ...value,
- ghost: {
- children: ,
- style: {
- minHeight: 0,
- width: 0,
+ expect(dndDispatch).toBeCalledWith({
+ type: 'startDragging',
+ payload: {
+ keyboardMode: true,
+ dragging: {
+ ...value,
+ ghost: {
+ children: ,
+ style: {
+ minHeight: 0,
+ width: 0,
+ },
+ },
},
},
});
- expect(setA11yMessage).toBeCalledWith('Lifted hello');
});
test('ActiveDropTarget gets ghost image', () => {
const onDrop = jest.fn();
- const setActiveDropTarget = jest.fn();
- const setA11yMessage = jest.fn();
const items = [
{
draggable: true,
@@ -504,19 +500,22 @@ describe('DragDrop', () => {
order: [2, 0, 1, 0],
},
];
+ const dndDispatch = jest.fn();
const component = mount(
Hello , style: {} } },
- setActiveDropTarget,
- setA11yMessage,
- activeDropTarget: { ...items[1].value, onDrop, dropType: 'move_compatible' },
- dropTargetsByOrder: {
- '2,0,1,0': { ...items[1].value, onDrop, dropType: 'move_compatible' },
+ value={[
+ {
+ ...defaultContextState,
+ dragging: { ...items[0].value, ghost: { children: Hello
, style: {} } },
+
+ activeDropTarget: { ...items[1].value, onDrop, dropType: 'move_compatible' },
+ dropTargetsByOrder: {
+ '2,0,1,0': { ...items[1].value, onDrop, dropType: 'move_compatible' },
+ },
+ keyboardMode: true,
},
- keyboardMode: true,
- }}
+ dndDispatch,
+ ]}
>
{items.map((props) => (
@@ -532,27 +531,25 @@ describe('DragDrop', () => {
describe('multiple drop targets', () => {
let activeDropTarget: DragContextState['activeDropTarget'];
+ const dragging = { id: '1', humanData: { label: 'Label1', layerNumber: 0 } };
const onDrop = jest.fn();
- let setActiveDropTarget = jest.fn();
- const setA11yMessage = jest.fn();
+ let dndDispatch = jest.fn();
let component: ReactWrapper;
beforeEach(() => {
activeDropTarget = undefined;
- setActiveDropTarget = jest.fn((val) => {
+ dndDispatch = jest.fn((val) => {
activeDropTarget = value as DragContextState['activeDropTarget'];
});
component = mount(
true}
- dropTargetsByOrder={undefined}
- registerDropTarget={jest.fn()}
+ value={[
+ {
+ ...defaultContextState,
+ dragging,
+ activeDropTarget,
+ },
+ dndDispatch,
+ ]}
>
{
.find('[data-test-subj="testDragDrop"]')
.first()
.simulate('dragstart', { dataTransfer });
-
+ dndDispatch.mockClear();
component.find('SingleDropInner').at(0).simulate('dragover');
- expect(setActiveDropTarget).toBeCalledWith({
- ...value,
- dropType: 'move_compatible',
- onDrop,
+ expect(dndDispatch).toBeCalledWith({
+ type: 'selectDropTarget',
+ payload: {
+ dragging,
+ dropTarget: {
+ ...value,
+ dropType: 'move_compatible',
+ onDrop,
+ },
+ },
});
+ dndDispatch.mockClear();
component.find('SingleDropInner').at(1).simulate('dragover');
-
- expect(setActiveDropTarget).toBeCalledWith({
- ...value,
- dropType: 'duplicate_compatible',
- onDrop,
+ expect(dndDispatch).toBeCalledWith({
+ type: 'selectDropTarget',
+ payload: {
+ dragging,
+ dropTarget: {
+ ...value,
+ dropType: 'duplicate_compatible',
+ onDrop,
+ },
+ },
});
+ dndDispatch.mockClear();
component.find('SingleDropInner').at(2).simulate('dragover');
- expect(setActiveDropTarget).toBeCalledWith({
- ...value,
- dropType: 'swap_compatible',
- onDrop,
+ expect(dndDispatch).toBeCalledWith({
+ type: 'selectDropTarget',
+ payload: {
+ dragging,
+ dropTarget: {
+ ...value,
+ dropType: 'swap_compatible',
+ onDrop,
+ },
+ },
});
+ dndDispatch.mockClear();
component.find('SingleDropInner').at(2).simulate('dragleave');
- expect(setActiveDropTarget).toBeCalledWith(undefined);
+ expect(dndDispatch).toBeCalledWith({ type: 'leaveDropTarget' });
});
test('drop on extra drop target passes correct dropType to onDrop', () => {
@@ -673,10 +690,16 @@ describe('DragDrop', () => {
.at(0)
.simulate('dragover', { altKey: true })
.simulate('dragover', { altKey: true });
- expect(setActiveDropTarget).toBeCalledWith({
- ...value,
- dropType: 'duplicate_compatible',
- onDrop,
+ expect(dndDispatch).toBeCalledWith({
+ type: 'selectDropTarget',
+ payload: {
+ dragging,
+ dropTarget: {
+ ...value,
+ dropType: 'duplicate_compatible',
+ onDrop,
+ },
+ },
});
component
@@ -684,10 +707,17 @@ describe('DragDrop', () => {
.at(0)
.simulate('dragover', { shiftKey: true })
.simulate('dragover', { shiftKey: true });
- expect(setActiveDropTarget).toBeCalledWith({
- ...value,
- dropType: 'swap_compatible',
- onDrop,
+
+ expect(dndDispatch).toBeCalledWith({
+ type: 'selectDropTarget',
+ payload: {
+ dragging,
+ dropTarget: {
+ ...value,
+ dropType: 'swap_compatible',
+ onDrop,
+ },
+ },
});
});
@@ -702,8 +732,10 @@ describe('DragDrop', () => {
extraDrop.simulate('dragover', { shiftKey: true });
extraDrop.simulate('dragover');
expect(
- setActiveDropTarget.mock.calls.every((call) => call[0].dropType === 'duplicate_compatible')
- );
+ dndDispatch.mock.calls.every((call) => {
+ return call[0].payload.dropTarget.dropType === 'duplicate_compatible';
+ })
+ ).toBe(true);
});
describe('keyboard navigation', () => {
const items = [
@@ -781,26 +813,26 @@ describe('DragDrop', () => {
};
test('when pressing enter key, context receives the proper dropTargetsByOrder', () => {
let dropTargetsByOrder: DragContextState['dropTargetsByOrder'] = {};
- const setKeyboardMode = jest.fn();
+
component = mount(
, style: {} } },
- setDragging: jest.fn(),
- setActiveDropTarget,
- setA11yMessage,
- activeDropTarget,
- dropTargetsByOrder,
- keyboardMode: true,
- setKeyboardMode,
- registerDropTarget: jest.fn((order, dropTarget) => {
- dropTargetsByOrder = {
- ...dropTargetsByOrder,
- [order.join(',')]: dropTarget,
- };
+ value={[
+ {
+ ...defaultContextState,
+ dragging: { ...items[0].value, ghost: { children: , style: {} } },
+ activeDropTarget,
+ dropTargetsByOrder,
+ keyboardMode: true,
+ },
+ jest.fn((action) => {
+ if (action.type === 'registerDropTargets') {
+ dropTargetsByOrder = {
+ ...dropTargetsByOrder,
+ ...action.payload,
+ };
+ }
}),
- }}
+ ]}
>
{items.map((props) => (
@@ -819,18 +851,16 @@ describe('DragDrop', () => {
test('when pressing ArrowRight key with modifier key pressed in, the extra drop target is selected', () => {
component = mount(
, style: {} } },
- setDragging: jest.fn(),
- setActiveDropTarget,
- setA11yMessage,
- activeDropTarget: undefined,
- dropTargetsByOrder: assignedDropTargetsByOrder,
- keyboardMode: true,
- setKeyboardMode: jest.fn(),
- registerDropTarget: jest.fn(),
- }}
+ value={[
+ {
+ ...defaultContextState,
+ dragging: { ...dragging, ghost: { children: , style: {} } },
+ activeDropTarget: undefined,
+ keyboardMode: true,
+ dropTargetsByOrder: assignedDropTargetsByOrder,
+ },
+ dndDispatch,
+ ]}
>
{items.map((props) => (
@@ -839,44 +869,56 @@ describe('DragDrop', () => {
))}
);
+ dndDispatch.mockClear();
act(() => {
component
.find('[data-test-subj="testDragDrop-keyboardHandler"]')
.at(1)
.simulate('keydown', { key: 'ArrowRight', altKey: true });
});
- expect(setActiveDropTarget).toBeCalledWith({
- ...items[1].value,
- onDrop,
- dropType: 'duplicate_compatible',
+ expect(dndDispatch).toBeCalledWith({
+ type: 'selectDropTarget',
+ payload: {
+ dragging: items[0].value,
+ dropTarget: {
+ ...items[1].value,
+ onDrop,
+ dropType: 'duplicate_compatible',
+ },
+ },
});
+ dndDispatch.mockClear();
act(() => {
component
.find('[data-test-subj="testDragDrop-keyboardHandler"]')
.at(1)
.simulate('keydown', { key: 'ArrowRight', shiftKey: true });
});
- expect(setActiveDropTarget).toBeCalledWith({
- ...items[1].value,
- onDrop,
- dropType: 'swap_compatible',
+ expect(dndDispatch).toBeCalledWith({
+ type: 'selectDropTarget',
+ payload: {
+ dragging: items[0].value,
+ dropTarget: {
+ ...items[1].value,
+ onDrop,
+ dropType: 'swap_compatible',
+ },
+ },
});
});
test('when having a main target selected and pressing alt, the first extra drop target is selected', () => {
component = mount(
, style: {} } },
- setDragging: jest.fn(),
- setActiveDropTarget,
- setA11yMessage,
- activeDropTarget: assignedDropTargetsByOrder['2,0,1,0,0'],
- dropTargetsByOrder: assignedDropTargetsByOrder,
- keyboardMode: true,
- setKeyboardMode: jest.fn(),
- registerDropTarget: jest.fn(),
- }}
+ value={[
+ {
+ ...defaultContextState,
+ dragging: { ...items[0].value, ghost: { children: , style: {} } },
+ activeDropTarget: assignedDropTargetsByOrder['2,0,1,0,0'],
+ dropTargetsByOrder: assignedDropTargetsByOrder,
+ keyboardMode: true,
+ },
+ dndDispatch,
+ ]}
>
{items.map((props) => (
@@ -885,44 +927,56 @@ describe('DragDrop', () => {
))}