diff --git a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_component.tsx b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_component.tsx index 972500c36475b..7cf818e9d826f 100644 --- a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_component.tsx +++ b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_component.tsx @@ -141,16 +141,13 @@ export class SearchableListContainerComponentInner extends Component - - - this.updateSearch(ev.target.value)} - /> - - - + + this.updateSearch(ev.target.value)} + /> + ); } diff --git a/src/plugins/presentation_util/public/components/input_controls/control_group/control_frame/control_frame.tsx b/src/plugins/presentation_util/public/components/input_controls/control_group/control_frame/control_frame.tsx index 90b632d1d9475..417e3adc83d75 100644 --- a/src/plugins/presentation_util/public/components/input_controls/control_group/control_frame/control_frame.tsx +++ b/src/plugins/presentation_util/public/components/input_controls/control_group/control_frame/control_frame.tsx @@ -9,7 +9,7 @@ import React, { useMemo } from 'react'; import useMount from 'react-use/lib/useMount'; import classNames from 'classnames'; -import { EuiFormControlLayout, EuiFormLabel, EuiFormRow } from '@elastic/eui'; +import { EuiFormControlLayout, EuiFormLabel, EuiFormRow, EuiIcon } from '@elastic/eui'; import { InputControlEmbeddable } from '../../embeddable/types'; @@ -31,7 +31,10 @@ export const ControlFrame = ({ twoLine, embeddable }: ControlFrameProps) => { fullWidth prepend={ twoLine ? undefined : ( - {embeddable.getInput().title} + + + {embeddable.getInput().title} + ) } > @@ -46,8 +49,15 @@ export const ControlFrame = ({ twoLine, embeddable }: ControlFrameProps) => { ); + const twoLineLabel = ( + <> + {embeddable.getInput().title} + + + ); + return ( - + {form} ); diff --git a/src/plugins/presentation_util/public/components/input_controls/control_group/control_group.scss b/src/plugins/presentation_util/public/components/input_controls/control_group/control_group.scss index 069fef745924e..f783b78d19642 100644 --- a/src/plugins/presentation_util/public/components/input_controls/control_group/control_group.scss +++ b/src/plugins/presentation_util/public/components/input_controls/control_group/control_group.scss @@ -17,6 +17,17 @@ .controlFrame--formControlLayout { width: 100%; min-width: $euiSize * 14; + .controlFrame__prepend { + padding-left: $euiSizeXS; + } + .controlFrame__prependIcon { + margin-top: - $euiSizeXS; + } +} + +.controlFrame__labelIcon { + margin-left: $euiSizeXS; + margin-top: - $euiSizeXS; } .controlFrame--control { @@ -39,9 +50,13 @@ min-width: $euiSize * 12; } -.controlGroup--sortItem { - &-isDragging { - @include euiBottomShadow; - background: $euiColorEmptyShade; - } +// .controlGroup--sortItem { +// &-isDragging { +// @include euiBottomShadow; +// background: $euiColorEmptyShade; +// } +// } + +.controlGroup--sortItemDraggable { + padding: $euiSizeXS 0; } \ No newline at end of file diff --git a/src/plugins/presentation_util/public/components/input_controls/control_group/control_group_component.tsx b/src/plugins/presentation_util/public/components/input_controls/control_group/control_group_component.tsx index f82322e1c97d2..52e1f71994cee 100644 --- a/src/plugins/presentation_util/public/components/input_controls/control_group/control_group_component.tsx +++ b/src/plugins/presentation_util/public/components/input_controls/control_group/control_group_component.tsx @@ -83,7 +83,7 @@ export const ControlGroupComponent = ({ setControlMeta={setControlMeta} setControlStyle={setControlStyle} /> - + {controlMeta.map(({ embeddableId, width }) => ( { const [isManagementFlyoutVisible, setIsManagementFlyoutVisible] = useState(false); + const [isManagementMenuVisible, setIsManagementMenuVisible] = useState(false); + const [isSwitchChecked, setIsSwitchChecked] = useState(false); const onDragEnd = ({ source, destination }: DropResult) => { if (source && destination) { @@ -78,6 +84,112 @@ export const ManageControlGroupComponent = ({ } }; + const onSwitchChange = () => { + setIsSwitchChecked(!isSwitchChecked); + }; + + const closePopover = () => { + setIsManagementMenuVisible(false); + }; + + const panels = [ + { + id: 0, + title: 'Options', + items: [ + { + name: 'Layout', + panel: 1, + }, + { + name: 'Reorder', + panel: 2, + }, + { + name: Delete all, + icon: , + }, + { + isSeparator: true, + key: 'sep', + }, + { + name: 'Add control', + icon: 'plusInCircle', + }, + ], + }, + { + id: 1, + initialFocusedItemIndex: 1, + title: 'Options', + items: [ + { + name: 'Single line', + icon: controlStyle === 'oneLine' ? 'check' : 'empty', + onClick: () => { + setControlStyle('oneLine'); + }, + }, + { + name: 'Two line', + icon: controlStyle === 'twoLine' ? 'check' : 'empty', + onClick: () => { + setControlStyle('twoLine'); + }, + }, + ], + }, + { + id: 2, + title: 'Options', + content: ( + + + {controlMeta.map((currentControlMeta, index) => ( + + {(provided, state) => ( + + + + )} + + ))} + + + ), + }, + ]; + + const manageControlsIcon = ( + setIsManagementMenuVisible(!isManagementMenuVisible)} + > + Settings + + ); + const manageControlsButton = ( { - const { title, width } = currentControlMeta; + const { title } = currentControlMeta; return ( @@ -107,136 +219,195 @@ export const ManageControlGroupComponent = ({ - - {title} - - - - setControlMeta((currentControls) => { - currentControls[index].width = newWidth as ControlWidth; - return [...currentControls]; - }) - } - /> - - - - + {title} ); }; + const EditControlLineItem = ({ + currentControlMeta, + // dragHandleProps, + index, + }: { + currentControlMeta: InputControlMeta; + // dragHandleProps: any; + index: number; + }) => { + const { title, width } = currentControlMeta; + return ( + <> + {/* */} + {/*
+ +
*/} + + {!isSwitchChecked ? ( + + + setControlMeta((currentControls) => { + currentControls[index].width = newWidth as ControlWidth; + return [...currentControls]; + }) + } + /> + + ) : null} + + {ControlGroupStrings.management.getDeleteButtonTitle()} + + + ); + }; + const manageControlGroupFlyout = ( setIsManagementFlyoutVisible(false)} aria-labelledby="flyoutTitle" > - +

{ControlGroupStrings.management.getFlyoutTitle()}

- -

{ControlGroupStrings.management.getDesignTitle()}

-
- - setControlStyle(newControlStyle as ControlStyle)} - /> - - -

{ControlGroupStrings.management.getLayoutTitle()}

-
- - - - - - {ControlGroupStrings.management.controlWidth.getChangeAllControlWidthsTitle()} - - - - currentMeta?.width === controlMeta[0]?.width) - ? controlMeta[0]?.width - : '' - } - legend={ControlGroupStrings.management.controlWidth.getWidthSwitchLegend()} - options={widthOptions} - onChange={(newWidth: string) => - setControlMeta((currentControls) => { - currentControls.forEach((currentMeta) => { - currentMeta.width = newWidth as ControlWidth; - }); - return [...currentControls]; - }) - } + + setControlStyle(newControlStyle as ControlStyle)} + /> + + + <> + - - - - {ControlGroupStrings.management.getDeleteAllButtonTitle()} - - - + {isSwitchChecked ? ( + <> + + currentMeta?.width === controlMeta[0]?.width) + ? controlMeta[0]?.width + : '' + } + legend={ControlGroupStrings.management.controlWidth.getWidthSwitchLegend()} + options={widthOptions} + onChange={(newWidth: string) => + setControlMeta((currentControls) => { + currentControls.forEach((currentMeta) => { + currentMeta.width = newWidth as ControlWidth; + }); + return [...currentControls]; + }) + } + /> + + ) : null} + +
- + {controlMeta.map((currentControlMeta, index) => ( {(provided, state) => ( - - + + + + + } + > + + )} ))} + + + {ControlGroupStrings.management.getDeleteAllButtonTitle()} + ); + const manageControlGroupMenu = ( + + + + ); + return ( <> {manageControlsButton} {isManagementFlyoutVisible && manageControlGroupFlyout} + {manageControlGroupMenu} ); }; diff --git a/src/plugins/presentation_util/public/components/input_controls/control_group/control_group_strings.ts b/src/plugins/presentation_util/public/components/input_controls/control_group/control_group_strings.ts index 3f0d2c50413fb..3cc5b64ec7f90 100644 --- a/src/plugins/presentation_util/public/components/input_controls/control_group/control_group_strings.ts +++ b/src/plugins/presentation_util/public/components/input_controls/control_group/control_group_strings.ts @@ -26,6 +26,10 @@ export const ControlGroupStrings = { i18n.translate('presentationUtil.inputControls.controlGroup.management.layoutTitle', { defaultMessage: 'Layout', }), + getDeleteButtonTitle: () => + i18n.translate('presentationUtil.inputControls.controlGroup.management.deleteAll', { + defaultMessage: 'Delete control', + }), getDeleteAllButtonTitle: () => i18n.translate('presentationUtil.inputControls.controlGroup.management.deleteAll', { defaultMessage: 'Delete all', @@ -72,11 +76,11 @@ export const ControlGroupStrings = { ), getSingleLineTitle: () => i18n.translate('presentationUtil.inputControls.controlGroup.management.layout.singleLine', { - defaultMessage: 'Single line layout', + defaultMessage: 'Single line', }), getTwoLineTitle: () => i18n.translate('presentationUtil.inputControls.controlGroup.management.layout.twoLine', { - defaultMessage: 'Two line layout', + defaultMessage: 'Two line', }), }, }, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/cypress/integration/engines.spec.d.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/cypress/integration/engines.spec.d.ts new file mode 100644 index 0000000000000..cb0ff5c3b541f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/cypress/integration/engines.spec.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/cypress/integration/engines.spec.js b/x-pack/plugins/enterprise_search/public/applications/app_search/cypress/integration/engines.spec.js new file mode 100644 index 0000000000000..9baa1c9ca3507 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/cypress/integration/engines.spec.js @@ -0,0 +1,17 @@ +"use strict"; +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +Object.defineProperty(exports, "__esModule", { value: true }); +const commands_1 = require("../support/commands"); +context('Engines', () => { + beforeEach(() => { + commands_1.login(); + }); + it('renders', () => { + cy.contains('Engines'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/cypress/support/commands.d.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/cypress/support/commands.d.ts new file mode 100644 index 0000000000000..7ce3798c11efb --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/cypress/support/commands.d.ts @@ -0,0 +1,7 @@ +interface Login { + path?: string; + username?: string; + password?: string; +} +export declare const login: ({ path, ...args }?: Login) => void; +export {}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/cypress/support/commands.js b/x-pack/plugins/enterprise_search/public/applications/app_search/cypress/support/commands.js new file mode 100644 index 0000000000000..f4318b28eaecf --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/cypress/support/commands.js @@ -0,0 +1,16 @@ +"use strict"; +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.login = void 0; +const commands_1 = require("../../../shared/cypress/commands"); +const routes_1 = require("../../../shared/cypress/routes"); +const login = ({ path = '/', ...args } = {}) => { + commands_1.login({ ...args }); + cy.visit(`${routes_1.appSearchPath}${path}`); +}; +exports.login = login; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/cypress/integration/overview.spec.d.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/cypress/integration/overview.spec.d.ts new file mode 100644 index 0000000000000..cb0ff5c3b541f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/cypress/integration/overview.spec.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/cypress/integration/overview.spec.js b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/cypress/integration/overview.spec.js new file mode 100644 index 0000000000000..190f75b65767f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/cypress/integration/overview.spec.js @@ -0,0 +1,38 @@ +"use strict"; +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +Object.defineProperty(exports, "__esModule", { value: true }); +const commands_1 = require("../../../shared/cypress/commands"); +const routes_1 = require("../../../shared/cypress/routes"); +context('Enterprise Search Overview', () => { + beforeEach(() => { + commands_1.login(); + }); + it('should contain product cards', () => { + cy.visit(routes_1.overviewPath); + cy.contains('Welcome to Elastic Enterprise Search'); + cy.get('[data-test-subj="appSearchProductCard"]') + .contains('Launch App Search') + .should('have.attr', 'href') + .and('match', /app_search/); + cy.get('[data-test-subj="workplaceSearchProductCard"]') + .contains('Launch Workplace Search') + .should('have.attr', 'href') + .and('match', /workplace_search/); + }); + it('should have a setup guide', () => { + // @see https://github.com/quasarframework/quasar/issues/2233#issuecomment-492975745 + // This only appears to occur for setup guides - I haven't (yet?) run into it on other pages + cy.on('uncaught:exception', (err) => { + if (err.message.includes('> ResizeObserver loop limit exceeded')) + return false; + }); + cy.visit(`${routes_1.overviewPath}/setup_guide`); + cy.contains('Setup Guide'); + cy.contains('Add your Enterprise Search host URL to your Kibana configuration'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/cypress/commands.d.ts b/x-pack/plugins/enterprise_search/public/applications/shared/cypress/commands.d.ts new file mode 100644 index 0000000000000..8d53606677cce --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/cypress/commands.d.ts @@ -0,0 +1,6 @@ +interface Login { + username?: string; + password?: string; +} +export declare const login: ({ username, password, }?: Login) => void; +export {}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/cypress/commands.js b/x-pack/plugins/enterprise_search/public/applications/shared/cypress/commands.js new file mode 100644 index 0000000000000..414b4f0c58895 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/cypress/commands.js @@ -0,0 +1,23 @@ +"use strict"; +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.login = void 0; +const login = ({ username = Cypress.env('username'), password = Cypress.env('password'), } = {}) => { + cy.request({ + method: 'POST', + url: '/internal/security/login', + headers: { 'kbn-xsrf': 'cypress' }, + body: { + providerType: 'basic', + providerName: 'basic', + currentURL: '/', + params: { username, password }, + }, + }); +}; +exports.login = login; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/cypress/routes.d.ts b/x-pack/plugins/enterprise_search/public/applications/shared/cypress/routes.d.ts new file mode 100644 index 0000000000000..455d88cc22f58 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/cypress/routes.d.ts @@ -0,0 +1,3 @@ +export declare const overviewPath = "/app/enterprise_search/overview"; +export declare const appSearchPath = "/app/enterprise_search/app_search"; +export declare const workplaceSearchPath = "/app/enterprise_search/workplace_search"; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/cypress/routes.js b/x-pack/plugins/enterprise_search/public/applications/shared/cypress/routes.js new file mode 100644 index 0000000000000..8a71a53f8e43b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/cypress/routes.js @@ -0,0 +1,12 @@ +"use strict"; +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.workplaceSearchPath = exports.appSearchPath = exports.overviewPath = void 0; +exports.overviewPath = '/app/enterprise_search/overview'; +exports.appSearchPath = '/app/enterprise_search/app_search'; +exports.workplaceSearchPath = '/app/enterprise_search/workplace_search'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress/integration/overview.spec.d.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress/integration/overview.spec.d.ts new file mode 100644 index 0000000000000..cb0ff5c3b541f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress/integration/overview.spec.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress/integration/overview.spec.js b/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress/integration/overview.spec.js new file mode 100644 index 0000000000000..62ad09389a453 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress/integration/overview.spec.js @@ -0,0 +1,17 @@ +"use strict"; +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +Object.defineProperty(exports, "__esModule", { value: true }); +const commands_1 = require("../support/commands"); +context('Overview', () => { + beforeEach(() => { + commands_1.login(); + }); + it('renders', () => { + cy.contains('Workplace Search'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress/support/commands.d.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress/support/commands.d.ts new file mode 100644 index 0000000000000..7ce3798c11efb --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress/support/commands.d.ts @@ -0,0 +1,7 @@ +interface Login { + path?: string; + username?: string; + password?: string; +} +export declare const login: ({ path, ...args }?: Login) => void; +export {}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress/support/commands.js b/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress/support/commands.js new file mode 100644 index 0000000000000..fd6ce93bb983a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress/support/commands.js @@ -0,0 +1,16 @@ +"use strict"; +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.login = void 0; +const commands_1 = require("../../../shared/cypress/commands"); +const routes_1 = require("../../../shared/cypress/routes"); +const login = ({ path = '/', ...args } = {}) => { + commands_1.login({ ...args }); + cy.visit(`${routes_1.workplaceSearchPath}${path}`); +}; +exports.login = login;