[Time to Visualize] Add Discrete Library Option to Save Modal (#94589) (#94961)

* save modal UI and Redirect and save to library

Co-authored-by: Poff Poffenberger <poffdeluxe@gmail.com>
# Conflicts:
#	.github/CODEOWNERS
This commit is contained in:
Devon Thomson 2021-03-18 15:56:01 -04:00 committed by GitHub
parent 8b22bd10b3
commit 52e0a43e21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 1010 additions and 164 deletions

374
.github/CODEOWNERS vendored Normal file
View file

@ -0,0 +1,374 @@
# GitHub CODEOWNERS definition
# Identify which groups will be pinged by changes to different parts of the codebase.
# For more info, see https://help.github.com/articles/about-codeowners/
# The #CC# prefix delineates Code Coverage,
# used for the 'team' designator within Kibana Stats
# App
/x-pack/plugins/discover_enhanced/ @elastic/kibana-app
/x-pack/plugins/lens/ @elastic/kibana-app
/x-pack/plugins/graph/ @elastic/kibana-app
/src/plugins/advanced_settings/ @elastic/kibana-app
/src/plugins/charts/ @elastic/kibana-app
/src/plugins/discover/ @elastic/kibana-app
/src/plugins/management/ @elastic/kibana-app
/src/plugins/kibana_legacy/ @elastic/kibana-app
/src/plugins/timelion/ @elastic/kibana-app
/src/plugins/vis_default_editor/ @elastic/kibana-app
/src/plugins/vis_type_metric/ @elastic/kibana-app
/src/plugins/vis_type_table/ @elastic/kibana-app
/src/plugins/vis_type_tagcloud/ @elastic/kibana-app
/src/plugins/vis_type_timelion/ @elastic/kibana-app
/src/plugins/vis_type_timeseries/ @elastic/kibana-app
/src/plugins/vis_type_vega/ @elastic/kibana-app
/src/plugins/vis_type_vislib/ @elastic/kibana-app
/src/plugins/vis_type_xy/ @elastic/kibana-app
/src/plugins/visualize/ @elastic/kibana-app
/src/plugins/visualizations/ @elastic/kibana-app
/packages/kbn-tinymath/ @elastic/kibana-app
# Application Services
/examples/bfetch_explorer/ @elastic/kibana-app-services
/examples/dashboard_embeddable_examples/ @elastic/kibana-app-services
/examples/demo_search/ @elastic/kibana-app-services
/examples/developer_examples/ @elastic/kibana-app-services
/examples/embeddable_examples/ @elastic/kibana-app-services
/examples/embeddable_explorer/ @elastic/kibana-app-services
/examples/state_containers_examples/ @elastic/kibana-app-services
/examples/ui_action_examples/ @elastic/kibana-app-services
/examples/ui_actions_explorer/ @elastic/kibana-app-services
/examples/url_generators_examples/ @elastic/kibana-app-services
/examples/url_generators_explorer/ @elastic/kibana-app-services
/packages/elastic-datemath/ @elastic/kibana-app-services
/packages/kbn-interpreter/ @elastic/kibana-app-services
/src/plugins/bfetch/ @elastic/kibana-app-services
/src/plugins/data/ @elastic/kibana-app-services
/src/plugins/embeddable/ @elastic/kibana-app-services
/src/plugins/expressions/ @elastic/kibana-app-services
/src/plugins/inspector/ @elastic/kibana-app-services
/src/plugins/kibana_react/ @elastic/kibana-app-services
/src/plugins/kibana_react/public/code_editor @elastic/kibana-presentation
/src/plugins/kibana_utils/ @elastic/kibana-app-services
/src/plugins/navigation/ @elastic/kibana-app-services
/src/plugins/share/ @elastic/kibana-app-services
/src/plugins/ui_actions/ @elastic/kibana-app-services
/x-pack/examples/ui_actions_enhanced_examples/ @elastic/kibana-app-services
/x-pack/plugins/data_enhanced/ @elastic/kibana-app-services
/x-pack/plugins/embeddable_enhanced/ @elastic/kibana-app-services
/x-pack/plugins/ui_actions_enhanced/ @elastic/kibana-app-services
/x-pack/plugins/runtime_fields @elastic/kibana-app-services
#CC# /src/plugins/bfetch/ @elastic/kibana-app-services
#CC# /src/plugins/index_pattern_management/ @elastic/kibana-app-services
#CC# /src/plugins/inspector/ @elastic/kibana-app-services
#CC# /src/plugins/share/ @elastic/kibana-app-services
#CC# /x-pack/plugins/drilldowns/ @elastic/kibana-app-services
#CC# /packages/kbn-interpreter/ @elastic/kibana-app-services
# APM
/x-pack/plugins/apm/ @elastic/apm-ui
/x-pack/test/functional/apps/apm/ @elastic/apm-ui
/x-pack/test/apm_api_integration/ @elastic/apm-ui
/src/plugins/apm_oss/ @elastic/apm-ui
/src/apm.js @elastic/kibana-core @vigneshshanmugam
/packages/kbn-apm-config-loader/ @elastic/kibana-core @vigneshshanmugam
#CC# /src/plugins/apm_oss/ @elastic/apm-ui
#CC# /x-pack/plugins/observability/ @elastic/apm-ui
# Uptime
/x-pack/plugins/uptime @elastic/uptime
/x-pack/test/functional_with_es_ssl/apps/uptime @elastic/uptime
/x-pack/test/functional/apps/uptime @elastic/uptime
/x-pack/test/api_integration/apis/uptime @elastic/uptime
# Client Side Monitoring / Uptime (lives in APM directories but owned by Uptime)
/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm @elastic/uptime
/x-pack/plugins/apm/e2e/cypress/integration/csm_dashboard.feature @elastic/uptime
/x-pack/plugins/apm/public/application/csmApp.tsx @elastic/uptime
/x-pack/plugins/apm/public/components/app/RumDashboard @elastic/uptime
/x-pack/plugins/apm/server/lib/rum_client @elastic/uptime
/x-pack/plugins/apm/server/routes/rum_client.ts @elastic/uptime
/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts @elastic/uptime
/x-pack/test/apm_api_integration/tests/csm/ @elastic/uptime
# Beats
/x-pack/plugins/beats_management/ @elastic/beats
#CC# /x-pack/plugins/beats_management/ @elastic/beats
# Presentation
/src/plugins/dashboard/ @elastic/kibana-presentation
/src/plugins/input_control_vis/ @elastic/kibana-presentation
/src/plugins/vis_type_markdown/ @elastic/kibana-presentation
/src/plugins/presentation_util/ @elastic/kibana-presentation
/test/functional/apps/dashboard/ @elastic/kibana-presentation
/x-pack/plugins/canvas/ @elastic/kibana-presentation
/x-pack/plugins/dashboard_enhanced/ @elastic/kibana-presentation
/x-pack/test/functional/apps/canvas/ @elastic/kibana-presentation
#CC# /src/plugins/kibana_react/public/code_editor/ @elastic/kibana-presentation
#CC# /x-pack/legacy/plugins/canvas/ @elastic/kibana-presentation
#CC# /x-pack/plugins/dashboard_mode @elastic/kibana-presentation
# Observability UIs
/x-pack/plugins/infra/ @elastic/logs-metrics-ui
/x-pack/plugins/fleet/ @elastic/fleet
/x-pack/plugins/observability/ @elastic/observability-ui
/x-pack/plugins/monitoring/ @elastic/stack-monitoring-ui
# Machine Learning
/x-pack/plugins/ml/ @elastic/ml-ui
/x-pack/test/functional/apps/ml/ @elastic/ml-ui
/x-pack/test/functional/services/ml/ @elastic/ml-ui
/x-pack/test/accessibility/apps/ml.ts @elastic/ml-ui
# ML team owns and maintains the transform plugin despite it living in the Elasticsearch management section.
/x-pack/plugins/transform/ @elastic/ml-ui
/x-pack/test/functional/apps/transform/ @elastic/ml-ui
/x-pack/test/functional/services/transform/ @elastic/ml-ui
/x-pack/test/accessibility/apps/transform.ts @elastic/ml-ui
/x-pack/test/api_integration_basic/apis/ml/ @elastic/ml-ui
/x-pack/test/functional_basic/apps/ml/ @elastic/ml-ui
/x-pack/test/api_integration_basic/apis/transform/ @elastic/ml-ui
/x-pack/test/functional_basic/apps/transform/ @elastic/ml-ui
# Maps
#CC# /x-pack/plugins/maps/ @elastic/kibana-gis
/x-pack/test/api_integration/apis/maps/ @elastic/kibana-gis
/x-pack/test/functional/apps/maps/ @elastic/kibana-gis
/x-pack/test/functional/es_archives/maps/ @elastic/kibana-gis
/x-pack/test/visual_regression/tests/maps/index.js @elastic/kibana-gis
#CC# /src/plugins/maps_legacy/ @elastic/kibana-gis
#CC# /x-pack/plugins/file_upload @elastic/kibana-gis
#CC# /x-pack/plugins/maps_legacy_licensing @elastic/kibana-gis
/src/plugins/tile_map/ @elastic/kibana-gis
/src/plugins/region_map/ @elastic/kibana-gis
# Operations
/src/dev/ @elastic/kibana-operations
/src/setup_node_env/ @elastic/kibana-operations
/packages/*eslint*/ @elastic/kibana-operations
/packages/*babel*/ @elastic/kibana-operations
/packages/kbn-dev-utils*/ @elastic/kibana-operations
/packages/kbn-es/ @elastic/kibana-operations
/packages/kbn-optimizer/ @elastic/kibana-operations
/packages/kbn-pm/ @elastic/kibana-operations
/packages/kbn-test/ @elastic/kibana-operations
/packages/kbn-ui-shared-deps/ @elastic/kibana-operations
/packages/kbn-es-archiver/ @elastic/kibana-operations
/packages/kbn-utils/ @elastic/kibana-operations
/src/cli/keystore/ @elastic/kibana-operations
/src/legacy/server/warnings/ @elastic/kibana-operations
/.ci/es-snapshots/ @elastic/kibana-operations
/.github/workflows/ @elastic/kibana-operations
/vars/ @elastic/kibana-operations
/.bazelignore @elastic/kibana-operations
/.bazeliskversion @elastic/kibana-operations
/.bazelrc @elastic/kibana-operations
/.bazelrc.common @elastic/kibana-operations
/.bazelversion @elastic/kibana-operations
/WORKSPACE.bazel @elastic/kibana-operations
#CC# /packages/kbn-expect/ @elastic/kibana-operations
# Quality Assurance
/src/dev/code_coverage @elastic/kibana-qa
/vars/*Coverage.groovy @elastic/kibana-qa
/test/functional/services/common @elastic/kibana-qa
/test/functional/services/lib @elastic/kibana-qa
/test/functional/services/remote @elastic/kibana-qa
# Core
/src/core/ @elastic/kibana-core
/src/plugins/saved_objects_tagging_oss @elastic/kibana-core
/config/kibana.yml @elastic/kibana-core
/x-pack/plugins/features/ @elastic/kibana-core
/x-pack/plugins/licensing/ @elastic/kibana-core
/x-pack/plugins/global_search/ @elastic/kibana-core
/x-pack/plugins/cloud/ @elastic/kibana-core
/x-pack/plugins/saved_objects_tagging/ @elastic/kibana-core
/x-pack/test/saved_objects_field_count/ @elastic/kibana-core
/x-pack/test/saved_object_tagging/ @elastic/kibana-core
/packages/kbn-config-schema/ @elastic/kibana-core
/packages/kbn-std/ @elastic/kibana-core
/packages/kbn-config/ @elastic/kibana-core
/packages/kbn-logging/ @elastic/kibana-core
/packages/kbn-legacy-logging/ @elastic/kibana-core
/src/legacy/server/config/ @elastic/kibana-core
/src/legacy/server/http/ @elastic/kibana-core
/src/legacy/server/logging/ @elastic/kibana-core
/src/plugins/status_page/ @elastic/kibana-core
/src/plugins/saved_objects_management/ @elastic/kibana-core
/src/dev/run_check_published_api_changes.ts @elastic/kibana-core
/src/plugins/home/public @elastic/kibana-core
/src/plugins/home/server/*.ts @elastic/kibana-core
/src/plugins/home/server/services/ @elastic/kibana-core
/src/plugins/kibana_overview/ @elastic/kibana-core
/x-pack/plugins/global_search_bar/ @elastic/kibana-core
#CC# /src/core/server/csp/ @elastic/kibana-core
#CC# /src/legacy/server/config/ @elastic/kibana-core
#CC# /src/legacy/server/http/ @elastic/kibana-core
#CC# /src/legacy/ui/public/documentation_links @elastic/kibana-core
#CC# /src/plugins/legacy_export/ @elastic/kibana-core
#CC# /src/plugins/xpack_legacy/ @elastic/kibana-core
#CC# /src/plugins/saved_objects/ @elastic/kibana-core
#CC# /src/plugins/status_page/ @elastic/kibana-core
#CC# /x-pack/plugins/cloud/ @elastic/kibana-core
#CC# /x-pack/plugins/features/ @elastic/kibana-core
#CC# /x-pack/plugins/global_search/ @elastic/kibana-core
#CC# /src/plugins/newsfeed @elastic/kibana-core
#CC# /src/plugins/home/public @elastic/kibana-core
#CC# /src/plugins/home/server/services/ @elastic/kibana-core
#CC# /src/plugins/home/ @elastic/kibana-core
#CC# /x-pack/plugins/global_search_providers/ @elastic/kibana-core
# Kibana Telemetry
/packages/kbn-analytics/ @elastic/kibana-core
/packages/kbn-telemetry-tools/ @elastic/kibana-core
/src/plugins/kibana_usage_collection/ @elastic/kibana-core
/src/plugins/newsfeed/ @elastic/kibana-core
/src/plugins/telemetry/ @elastic/kibana-core
/src/plugins/telemetry_collection_manager/ @elastic/kibana-core
/src/plugins/telemetry_management_section/ @elastic/kibana-core
/src/plugins/usage_collection/ @elastic/kibana-core
/x-pack/plugins/telemetry_collection_xpack/ @elastic/kibana-core
/.telemetryrc.json @elastic/kibana-core
/x-pack/.telemetryrc.json @elastic/kibana-core
/src/plugins/telemetry/schema/ @elastic/kibana-core @elastic/kibana-telemetry
/x-pack/plugins/telemetry_collection_xpack/schema/ @elastic/kibana-core @elastic/kibana-telemetry
# Kibana Localization
/src/dev/i18n/ @elastic/kibana-localization @elastic/kibana-core
/src/core/public/i18n/ @elastic/kibana-localization @elastic/kibana-core
/packages/kbn-i18n/ @elastic/kibana-localization @elastic/kibana-core
#CC# /x-pack/plugins/translations/ @elastic/kibana-localization @elastic/kibana-core
# Security
/src/core/server/csp/ @elastic/kibana-security @elastic/kibana-core
/src/plugins/security_oss/ @elastic/kibana-security
/src/plugins/spaces_oss/ @elastic/kibana-security
/test/security_functional/ @elastic/kibana-security
/x-pack/plugins/spaces/ @elastic/kibana-security
/x-pack/plugins/encrypted_saved_objects/ @elastic/kibana-security
/x-pack/plugins/security/ @elastic/kibana-security
/x-pack/test/api_integration/apis/security/ @elastic/kibana-security
/x-pack/test/ui_capabilities/ @elastic/kibana-security
/x-pack/test/encrypted_saved_objects_api_integration/ @elastic/kibana-security
/x-pack/test/functional/apps/security/ @elastic/kibana-security
/x-pack/test/security_api_integration/ @elastic/kibana-security
/x-pack/test/security_functional/ @elastic/kibana-security
/x-pack/test/spaces_api_integration/ @elastic/kibana-security
/x-pack/test/saved_object_api_integration/ @elastic/kibana-security
#CC# /x-pack/plugins/security/ @elastic/kibana-security
# Kibana Alerting Services
/x-pack/plugins/alerting/ @elastic/kibana-alerting-services
/x-pack/plugins/actions/ @elastic/kibana-alerting-services
/x-pack/plugins/event_log/ @elastic/kibana-alerting-services
/x-pack/plugins/task_manager/ @elastic/kibana-alerting-services
/x-pack/test/alerting_api_integration/ @elastic/kibana-alerting-services
/x-pack/test/plugin_api_integration/test_suites/task_manager/ @elastic/kibana-alerting-services
/x-pack/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services
/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/ @elastic/kibana-alerting-services
/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/ @elastic/kibana-alerting-services
/docs/user/alerting/ @elastic/kibana-alerting-services
/docs/management/alerting/ @elastic/kibana-alerting-services
#CC# /x-pack/plugins/stack_alerts @elastic/kibana-alerting-services
# Enterprise Search
# Shared
/x-pack/plugins/enterprise_search/* @elastic/enterprise-search-frontend
/x-pack/plugins/enterprise_search/common/ @elastic/enterprise-search-frontend
/x-pack/plugins/enterprise_search/public/* @elastic/enterprise-search-frontend
/x-pack/plugins/enterprise_search/public/applications/* @elastic/enterprise-search-frontend
/x-pack/plugins/enterprise_search/public/applications/enterprise_search/ @elastic/enterprise-search-frontend
/x-pack/plugins/enterprise_search/public/applications/shared/ @elastic/enterprise-search-frontend
/x-pack/plugins/enterprise_search/public/applications/__mocks__/ @elastic/enterprise-search-frontend
/x-pack/plugins/enterprise_search/server/* @elastic/enterprise-search-frontend
/x-pack/plugins/enterprise_search/server/lib/ @elastic/enterprise-search-frontend
/x-pack/plugins/enterprise_search/server/__mocks__/ @elastic/enterprise-search-frontend
/x-pack/plugins/enterprise_search/server/collectors/enterprise_search/ @elastic/enterprise-search-frontend
/x-pack/plugins/enterprise_search/server/collectors/lib/ @elastic/enterprise-search-frontend
/x-pack/plugins/enterprise_search/server/routes/enterprise_search/ @elastic/enterprise-search-frontend
/x-pack/plugins/enterprise_search/server/saved_objects/enterprise_search/ @elastic/enterprise-search-frontend
/x-pack/test/functional_enterprise_search/ @elastic/enterprise-search-frontend
# App Search
/x-pack/plugins/enterprise_search/public/applications/app_search/ @elastic/app-search-frontend
/x-pack/plugins/enterprise_search/server/routes/app_search/ @elastic/app-search-frontend
/x-pack/plugins/enterprise_search/server/collectors/app_search/ @elastic/app-search-frontend
/x-pack/plugins/enterprise_search/server/saved_objects/app_search/ @elastic/app-search-frontend
# Workplace Search
/x-pack/plugins/enterprise_search/public/applications/workplace_search/ @elastic/workplace-search-frontend
/x-pack/plugins/enterprise_search/server/routes/workplace_search/ @elastic/workplace-search-frontend
/x-pack/plugins/enterprise_search/server/collectors/workplace_search/ @elastic/workplace-search-frontend
/x-pack/plugins/enterprise_search/server/saved_objects/workplace_search/ @elastic/workplace-search-frontend
# Elasticsearch UI
/src/plugins/dev_tools/ @elastic/es-ui
/src/plugins/console/ @elastic/es-ui
/src/plugins/es_ui_shared/ @elastic/es-ui
/x-pack/plugins/cross_cluster_replication/ @elastic/es-ui
/x-pack/plugins/index_lifecycle_management/ @elastic/es-ui
/x-pack/plugins/console_extensions/ @elastic/es-ui
/x-pack/plugins/grokdebugger/ @elastic/es-ui
/x-pack/plugins/index_management/ @elastic/es-ui
/x-pack/plugins/license_management/ @elastic/es-ui
/x-pack/plugins/painless_lab/ @elastic/es-ui
/x-pack/plugins/remote_clusters/ @elastic/es-ui
/x-pack/plugins/rollup/ @elastic/es-ui
/x-pack/plugins/searchprofiler/ @elastic/es-ui
/x-pack/plugins/snapshot_restore/ @elastic/es-ui
/x-pack/plugins/upgrade_assistant/ @elastic/es-ui
/x-pack/plugins/watcher/ @elastic/es-ui
/x-pack/plugins/ingest_pipelines/ @elastic/es-ui
/packages/kbn-ace/ @elastic/es-ui
/packages/kbn-monaco/ @elastic/es-ui
#CC# /x-pack/plugins/console_extensions/ @elastic/es-ui
#CC# /x-pack/plugins/cross_cluster_replication/ @elastic/es-ui
# Security Solution
/x-pack/test/endpoint_api_integration_no_ingest/ @elastic/security-solution
/x-pack/test/security_solution_endpoint/ @elastic/security-solution
/x-pack/test/functional/es_archives/endpoint/ @elastic/security-solution
/x-pack/test/plugin_functional/plugins/resolver_test/ @elastic/security-solution
/x-pack/test/plugin_functional/test_suites/resolver/ @elastic/security-solution
/x-pack/plugins/security_solution/ @elastic/security-solution
/x-pack/test/detection_engine_api_integration @elastic/security-solution
/x-pack/test/lists_api_integration @elastic/security-solution
/x-pack/test/api_integration/apis/security_solution @elastic/security-solution
#CC# /x-pack/plugins/security_solution/ @elastic/security-solution
# Security Solution sub teams
/x-pack/plugins/case @elastic/security-threat-hunting
/x-pack/test/case_api_integration @elastic/security-threat-hunting
/x-pack/plugins/lists @elastic/security-detections-response
# Security Intelligence And Analytics
/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules @elastic/security-intelligence-analytics
# Security Asset Management
/x-pack/plugins/osquery @elastic/security-asset-management
# Design (at the bottom for specificity of SASS files)
**/*.scss @elastic/kibana-design
#CC# /packages/kbn-ui-framework/ @elastic/kibana-design
# Observability design
/x-pack/plugins/apm/**/*.scss @elastic/observability-design
/x-pack/plugins/infra/**/*.scss @elastic/observability-design
/x-pack/plugins/fleet/**/*.scss @elastic/observability-design
/x-pack/plugins/observability/**/*.scss @elastic/observability-design
/x-pack/plugins/monitoring/**/*.scss @elastic/observability-design
# Ent. Search design
/x-pack/plugins/enterprise_search/**/*.scss @elastic/ent-search-design
# Security design
/x-pack/plugins/endpoint/**/*.scss @elastic/security-design
/x-pack/plugins/security_solution/**/*.scss @elastic/security-design
# Logstash
#CC# /x-pack/plugins/logstash/ @elastic/logstash
# Reporting
#CC# /x-pack/plugins/reporting/ @elastic/kibana-reporting-services

View file

@ -30,7 +30,7 @@ export interface SaveModalDashboardProps {
documentInfo: SaveModalDocumentInfo;
objectType: string;
onClose: () => void;
onSave: (props: OnSaveProps & { dashboardId: string | null }) => void;
onSave: (props: OnSaveProps & { dashboardId: string | null; addToLibrary: boolean }) => void;
tagOptions?: React.ReactNode | ((state: SaveModalState) => React.ReactNode);
}
@ -48,6 +48,9 @@ export function SavedObjectSaveModalDashboard(props: SaveModalDashboardProps) {
const [dashboardOption, setDashboardOption] = useState<'new' | 'existing' | null>(
documentId || disableDashboardOptions ? null : 'existing'
);
const [isAddToLibrarySelected, setAddToLibrary] = useState<boolean>(
!initialCopyOnSave || disableDashboardOptions
);
const [selectedDashboard, setSelectedDashboard] = useState<{ id: string; name: string } | null>(
null
);
@ -62,12 +65,13 @@ export function SavedObjectSaveModalDashboard(props: SaveModalDashboardProps) {
onChange={(option) => {
setDashboardOption(option);
}}
{...{ copyOnSave, documentId, dashboardOption }}
{...{ copyOnSave, documentId, dashboardOption, setAddToLibrary, isAddToLibrarySelected }}
/>
)
: null;
const onCopyOnSaveChange = (newCopyOnSave: boolean) => {
setAddToLibrary(true);
setDashboardOption(null);
setCopyOnSave(newCopyOnSave);
};
@ -85,7 +89,7 @@ export function SavedObjectSaveModalDashboard(props: SaveModalDashboardProps) {
}
}
props.onSave({ ...onSaveProps, dashboardId });
props.onSave({ ...onSaveProps, dashboardId, addToLibrary: isAddToLibrarySelected });
};
const saveLibraryLabel =
@ -113,7 +117,7 @@ export function SavedObjectSaveModalDashboard(props: SaveModalDashboardProps) {
onSave={onModalSave}
title={documentInfo.title}
showCopyOnSave={documentId ? true : false}
options={dashboardOption === null ? tagOptions : undefined} // Show tags when not adding to dashboard
options={isAddToLibrarySelected ? tagOptions : undefined} // Show tags when not adding to dashboard
description={documentInfo.description}
showDescription={true}
{...{

View file

@ -44,6 +44,7 @@ export function Example({
hasDocumentId: boolean;
} & StorybookParams) {
const [dashboardOption, setDashboardOption] = useState<'new' | 'existing' | null>('existing');
const [isAddToLibrarySelected, setAddToLibrary] = useState(false);
return (
<SaveModalDashboardSelector
@ -52,6 +53,8 @@ export function Example({
dashboardOption={dashboardOption}
copyOnSave={copyOnSave}
documentId={hasDocumentId ? 'abc' : undefined}
isAddToLibrarySelected={isAddToLibrarySelected}
setAddToLibrary={setAddToLibrary}
/>
);
}

View file

@ -19,6 +19,7 @@ import {
EuiIconTip,
EuiPanel,
EuiSpacer,
EuiCheckbox,
} from '@elastic/eui';
import { DashboardPicker, DashboardPickerProps } from './dashboard_picker';
@ -29,24 +30,105 @@ export interface SaveModalDashboardSelectorProps {
copyOnSave: boolean;
documentId?: string;
onSelectDashboard: DashboardPickerProps['onChange'];
setAddToLibrary: (selected: boolean) => void;
isAddToLibrarySelected: boolean;
dashboardOption: 'new' | 'existing' | null;
onChange: (dashboardOption: 'new' | 'existing' | null) => void;
}
export function SaveModalDashboardSelector(props: SaveModalDashboardSelectorProps) {
const { documentId, onSelectDashboard, dashboardOption, onChange, copyOnSave } = props;
const {
documentId,
onSelectDashboard,
setAddToLibrary,
isAddToLibrarySelected,
dashboardOption,
onChange,
copyOnSave,
} = props;
const isDisabled = !copyOnSave && !!documentId;
return (
<>
<EuiFormRow
label={
<FormattedMessage
id="presentationUtil.saveModalDashboard.addToDashboardLabel"
defaultMessage="Add to dashboard"
/>
}
hasChildLabel={false}
>
<>
<EuiPanel color="subdued" hasShadow={false} data-test-subj="add-to-dashboard-options">
<div>
<>
<EuiRadio
checked={dashboardOption === 'existing'}
id="existing-dashboard-option"
name="dashboard-option"
label={i18n.translate(
'presentationUtil.saveModalDashboard.existingDashboardOptionLabel',
{
defaultMessage: 'Existing',
}
)}
onChange={() => onChange('existing')}
disabled={isDisabled}
/>
<div className="savAddDashboard__searchDashboards">
<DashboardPicker
isDisabled={dashboardOption !== 'existing'}
onChange={onSelectDashboard}
/>
</div>
<EuiSpacer size="s" />
</>
<>
<EuiRadio
checked={dashboardOption === 'new'}
id="new-dashboard-option"
name="dashboard-option"
label={i18n.translate(
'presentationUtil.saveModalDashboard.newDashboardOptionLabel',
{
defaultMessage: 'New',
}
)}
onChange={() => onChange('new')}
disabled={isDisabled}
/>
<EuiSpacer size="s" />
</>
<EuiRadio
checked={dashboardOption === null}
id="add-to-library-option"
name="dashboard-option"
label={i18n.translate(
'presentationUtil.saveModalDashboard.noDashboardOptionLabel',
{
defaultMessage: 'None',
}
)}
onChange={() => {
setAddToLibrary(true);
onChange(null);
}}
disabled={isDisabled}
/>
</div>
</EuiPanel>
<EuiSpacer size="s" />
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false}>
<EuiFlexItem grow={false}>
<FormattedMessage
id="presentationUtil.saveModalDashboard.addToDashboardLabel"
defaultMessage="Add to dashboard"
<EuiFlexItem grow={false} data-test-subj="add-to-library-checkbox">
<EuiCheckbox
id="add-to-library-checkbox"
label={i18n.translate('presentationUtil.saveModalDashboard.libraryOptionLabel', {
defaultMessage: 'Add to library',
})}
checked={isAddToLibrarySelected}
disabled={dashboardOption === null || isDisabled}
onChange={(event) => setAddToLibrary(event.target.checked)}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
@ -55,67 +137,13 @@ export function SaveModalDashboardSelector(props: SaveModalDashboardSelectorProp
content={
<FormattedMessage
id="presentationUtil.saveModalDashboard.dashboardInfoTooltip"
defaultMessage="Items added to a dashboard will not appear in the library and must be edited from the dashboard."
defaultMessage="items added to the Visualize Library are available to all dashboards. Edits to a library item appear everywhere it is used."
/>
}
/>
</EuiFlexItem>
</EuiFlexGroup>
}
hasChildLabel={false}
>
<EuiPanel color="subdued" hasShadow={false} data-test-subj="add-to-dashboard-options">
<div>
<>
<EuiRadio
checked={dashboardOption === 'existing'}
id="existing-dashboard-option"
name="dashboard-option"
label={i18n.translate(
'presentationUtil.saveModalDashboard.existingDashboardOptionLabel',
{
defaultMessage: 'Existing',
}
)}
onChange={() => onChange('existing')}
disabled={isDisabled}
/>
<div className="savAddDashboard__searchDashboards">
<DashboardPicker
isDisabled={dashboardOption !== 'existing'}
onChange={onSelectDashboard}
/>
</div>
<EuiSpacer size="s" />
</>
<>
<EuiRadio
checked={dashboardOption === 'new'}
id="new-dashboard-option"
name="dashboard-option"
label={i18n.translate(
'presentationUtil.saveModalDashboard.newDashboardOptionLabel',
{
defaultMessage: 'New',
}
)}
onChange={() => onChange('new')}
disabled={isDisabled}
/>
<EuiSpacer size="s" />
</>
<EuiRadio
checked={dashboardOption === null}
id="add-to-library-option"
name="dashboard-option"
label={i18n.translate('presentationUtil.saveModalDashboard.libraryOptionLabel', {
defaultMessage: 'No dashboard, but add to library',
})}
onChange={() => onChange(null)}
disabled={isDisabled}
/>
</div>
</EuiPanel>
</>
</EuiFormRow>
</>
);

View file

@ -93,7 +93,7 @@ export const getTopNavConfig = (
/**
* Called when the user clicks "Save" button.
*/
async function doSave(saveOptions: SavedObjectSaveOpts) {
async function doSave(saveOptions: SavedObjectSaveOpts & { dashboardId?: string }) {
const newlyCreated = !Boolean(savedVis.id) || savedVis.copyOnSave;
// vis.title was not bound and it's needed to reflect title into visState
stateContainer.transitions.setVis({
@ -118,7 +118,7 @@ export const getTopNavConfig = (
'data-test-subj': 'saveVisualizationSuccess',
});
if (originatingApp && saveOptions.returnToOrigin) {
if ((originatingApp && saveOptions.returnToOrigin) || saveOptions.dashboardId) {
if (!embeddableId) {
const appPath = `${VisualizeConstants.EDIT_PATH}/${encodeURIComponent(id)}`;
@ -127,16 +127,26 @@ export const getTopNavConfig = (
setActiveUrl(appPath);
}
const app = originatingApp || 'dashboards';
let path;
if (saveOptions.dashboardId) {
path =
saveOptions.dashboardId === 'new' ? '#/create' : `#/view/${saveOptions.dashboardId}`;
}
if (newlyCreated && stateTransfer) {
stateTransfer.navigateToWithEmbeddablePackage(originatingApp, {
stateTransfer.navigateToWithEmbeddablePackage(app, {
state: {
type: VISUALIZE_EMBEDDABLE_TYPE,
input: { savedObjectId: id },
embeddableId,
},
path,
});
} else {
application.navigateToApp(originatingApp);
// TODO: need the same thing here?
application.navigateToApp(app, { path });
}
} else {
if (setOriginatingApp && originatingApp && newlyCreated) {
@ -321,7 +331,11 @@ export const getTopNavConfig = (
newDescription,
returnToOrigin,
dashboardId,
}: OnSaveProps & { returnToOrigin?: boolean } & { dashboardId?: string | null }) => {
addToLibrary,
}: OnSaveProps & { returnToOrigin?: boolean } & {
dashboardId?: string | null;
addToLibrary?: boolean;
}) => {
const currentTitle = savedVis.title;
savedVis.title = newTitle;
embeddableHandler.updateInput({ title: newTitle });
@ -337,9 +351,12 @@ export const getTopNavConfig = (
isTitleDuplicateConfirmed,
onTitleDuplicate,
returnToOrigin,
dashboardId: !!dashboardId ? dashboardId : undefined,
};
if (dashboardId) {
// If we're adding to a dashboard and not saving to library,
// we'll want to use a by-value operation
if (dashboardId && !addToLibrary) {
const appPath = `${VisualizeConstants.LANDING_PAGE_PATH}`;
// Manually insert a new url so the back button will open the saved visualization.
@ -369,6 +386,8 @@ export const getTopNavConfig = (
return { id: true };
}
// We're adding the viz to a library so we need to save it and then
// add to a dashboard if necessary
const response = await doSave(saveOptions);
// If the save wasn't successful, put the original values back.
if (!response.id || response.error) {

View file

@ -26,7 +26,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
]);
describe('Add to Dashboard', function describeIndexTests() {
it('adding a new metric to a new dashboard', async function () {
it('adding a new metric to a new dashboard by value', async function () {
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickMetric();
await PageObjects.visualize.clickNewSearch();
@ -36,6 +36,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.timeToVisualize.saveFromModal('My New Vis 1', {
addToDashboard: 'new',
saveToLibrary: false,
});
await PageObjects.dashboard.waitForRenderComplete();
@ -43,10 +44,39 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists('My New Vis 1');
expect(isLinked).to.be(false);
await PageObjects.timeToVisualize.resetNewDashboard();
});
it('adding a existing metric to a new dashboard', async function () {
it('adding a new metric to a new dashboard by reference', async function () {
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickMetric();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
await testSubjects.click('visualizeSaveButton');
await PageObjects.timeToVisualize.saveFromModal('My Saved New Vis 1', {
addToDashboard: 'new',
saveToLibrary: true,
});
await PageObjects.dashboard.waitForRenderComplete();
await dashboardExpect.metricValuesExist(['14,004']);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
'My Saved New Vis 1'
);
expect(isLinked).to.be(true);
await PageObjects.timeToVisualize.resetNewDashboard();
});
it('adding a existing metric to a new dashboard by value', async function () {
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickMetric();
await PageObjects.visualize.clickNewSearch();
@ -57,6 +87,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
// Save this new viz to library
await PageObjects.timeToVisualize.saveFromModal('My New Vis 1', {
addToDashboard: null,
saveToLibrary: true,
});
await testSubjects.click('visualizeSaveButton');
@ -68,6 +99,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.timeToVisualize.saveFromModal('My New Vis 1 Copy', {
addToDashboard: 'new',
saveAsNew: true,
saveToLibrary: false,
});
await PageObjects.dashboard.waitForRenderComplete();
@ -75,10 +107,85 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
'My New Vis 1 Copy'
);
expect(isLinked).to.be(false);
await PageObjects.timeToVisualize.resetNewDashboard();
});
it('adding a new metric to an existing dashboard', async function () {
it('adding a existing metric to a new dashboard by reference', async function () {
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickMetric();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
await testSubjects.click('visualizeSaveButton');
// Save this new viz to library
await PageObjects.timeToVisualize.saveFromModal('Another New Vis 1', {
addToDashboard: null,
saveToLibrary: true,
});
await testSubjects.click('visualizeSaveButton');
// All the options should be disabled
await PageObjects.timeToVisualize.ensureDashboardOptionsAreDisabled();
// Save a new copy of this viz to a new dashboard
await PageObjects.timeToVisualize.saveFromModal('Another New Vis 1 Copy', {
addToDashboard: 'new',
saveAsNew: true,
saveToLibrary: true,
});
await PageObjects.dashboard.waitForRenderComplete();
await dashboardExpect.metricValuesExist(['14,004']);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
'Another New Vis 1 Copy'
);
expect(isLinked).to.be(true);
await PageObjects.timeToVisualize.resetNewDashboard();
});
it('adding a new metric to an existing dashboard by value', async function () {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.addVisualizations(['Visualization AreaChart']);
await PageObjects.dashboard.saveDashboard('My Excellent Dashboard');
await PageObjects.dashboard.gotoDashboardLandingPage();
await listingTable.searchAndExpectItemsCount('dashboard', 'My Excellent Dashboard', 1);
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickMetric();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
await testSubjects.click('visualizeSaveButton');
await PageObjects.timeToVisualize.saveFromModal('My New Vis 2', {
addToDashboard: 'existing',
dashboardId: 'My Excellent Dashboard',
saveToLibrary: false,
});
await PageObjects.dashboard.waitForRenderComplete();
await dashboardExpect.metricValuesExist(['14,004']);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(2);
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists('My New Vis 2');
expect(isLinked).to.be(false);
});
it('adding a new metric to an existing dashboard by reference', async function () {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
@ -94,18 +201,24 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await testSubjects.click('visualizeSaveButton');
await PageObjects.timeToVisualize.saveFromModal('My New Vis 2', {
await PageObjects.timeToVisualize.saveFromModal('My Saved New Vis 2', {
addToDashboard: 'existing',
dashboardId: 'My Wonderful Dashboard',
saveToLibrary: true,
});
await PageObjects.dashboard.waitForRenderComplete();
await dashboardExpect.metricValuesExist(['14,004']);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(2);
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
'My Saved New Vis 2'
);
expect(isLinked).to.be(true);
});
it('adding a existing metric to an existing dashboard', async function () {
it('adding a existing metric to an existing dashboard by value', async function () {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
@ -124,6 +237,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
// Save this new viz to library
await PageObjects.timeToVisualize.saveFromModal('My New Vis 2', {
addToDashboard: null,
saveToLibrary: true,
});
await testSubjects.click('visualizeSaveButton');
@ -136,12 +250,64 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
addToDashboard: 'existing',
dashboardId: 'My Very Cool Dashboard',
saveAsNew: true,
saveToLibrary: false,
});
await PageObjects.dashboard.waitForRenderComplete();
await dashboardExpect.metricValuesExist(['14,004']);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(2);
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
'My New Vis 2 Copy'
);
expect(isLinked).to.be(false);
});
it('adding a existing metric to an existing dashboard by reference', async function () {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.addVisualizations(['Visualization AreaChart']);
await PageObjects.dashboard.saveDashboard('My Very Neat Dashboard');
await PageObjects.dashboard.gotoDashboardLandingPage();
await listingTable.searchAndExpectItemsCount('dashboard', 'My Very Neat Dashboard', 1);
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickMetric();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
await testSubjects.click('visualizeSaveButton');
// Save this new viz to library
await PageObjects.timeToVisualize.saveFromModal('Neat Saved Vis 2', {
addToDashboard: null,
saveToLibrary: true,
});
await testSubjects.click('visualizeSaveButton');
// All the options should be disabled
await PageObjects.timeToVisualize.ensureDashboardOptionsAreDisabled();
// Save a new copy of this viz to an existing dashboard
await PageObjects.timeToVisualize.saveFromModal('Neat Saved Vis 2 Copy', {
addToDashboard: 'existing',
dashboardId: 'My Very Neat Dashboard',
saveAsNew: true,
saveToLibrary: true,
});
await PageObjects.dashboard.waitForRenderComplete();
await dashboardExpect.metricValuesExist(['14,004']);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(2);
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
'Neat Saved Vis 2 Copy'
);
expect(isLinked).to.be(true);
});
});
}

View file

@ -10,6 +10,7 @@ import { FtrProviderContext } from '../ftr_provider_context';
interface SaveModalArgs {
addToDashboard?: 'new' | 'existing' | null;
saveToLibrary?: boolean;
dashboardId?: string;
saveAsNew?: boolean;
redirectToOrigin?: boolean;
@ -35,7 +36,9 @@ export function TimeToVisualizePageProvider({ getService, getPageObjects }: FtrP
const dashboardSelector = await testSubjects.find('add-to-dashboard-options');
await dashboardSelector.findByCssSelector(`input[id="new-dashboard-option"]:disabled`);
await dashboardSelector.findByCssSelector(`input[id="existing-dashboard-option"]:disabled`);
await dashboardSelector.findByCssSelector(`input[id="add-to-library-option"]:disabled`);
const librarySelector = await testSubjects.find('add-to-library-checkbox');
await librarySelector.findByCssSelector(`input[id="add-to-library-checkbox"]:disabled`);
}
public async resetNewDashboard() {
@ -46,7 +49,13 @@ export function TimeToVisualizePageProvider({ getService, getPageObjects }: FtrP
public async setSaveModalValues(
vizName: string,
{ saveAsNew, redirectToOrigin, addToDashboard, dashboardId }: SaveModalArgs = {}
{
saveAsNew,
redirectToOrigin,
addToDashboard,
dashboardId,
saveToLibrary,
}: SaveModalArgs = {}
) {
await testSubjects.setValue('savedObjectTitle', vizName);
@ -57,13 +66,6 @@ export function TimeToVisualizePageProvider({ getService, getPageObjects }: FtrP
await testSubjects.setEuiSwitch('saveAsNewCheckbox', state);
}
const hasRedirectToOrigin = await testSubjects.exists('returnToOriginModeSwitch');
if (hasRedirectToOrigin && redirectToOrigin !== undefined) {
const state = redirectToOrigin ? 'check' : 'uncheck';
log.debug('redirect to origin checkbox exists. Setting its state to', state);
await testSubjects.setEuiSwitch('returnToOriginModeSwitch', state);
}
const hasDashboardSelector = await testSubjects.exists('add-to-dashboard-options');
if (hasDashboardSelector && addToDashboard !== undefined) {
let option: DashboardPickerOption = 'add-to-library-option';
@ -80,6 +82,40 @@ export function TimeToVisualizePageProvider({ getService, getPageObjects }: FtrP
await find.clickByButtonText(dashboardId);
}
}
const hasSaveToLibrary = await testSubjects.exists('add-to-library-checkbox');
if (hasSaveToLibrary && saveToLibrary !== undefined) {
const libraryCheckbox = await find.byCssSelector('#add-to-library-checkbox');
const isChecked = await libraryCheckbox.isSelected();
const needsClick = isChecked !== saveToLibrary;
const state = saveToLibrary ? 'check' : 'uncheck';
log.debug('save to library checkbox exists. Setting its state to', state);
if (needsClick) {
const selector = await testSubjects.find('add-to-library-checkbox');
const label = await selector.findByCssSelector(`label[for="add-to-library-checkbox"]`);
await label.click();
}
}
const hasRedirectToOrigin = await testSubjects.exists('returnToOriginModeSwitch');
if (hasRedirectToOrigin && redirectToOrigin !== undefined) {
const state = redirectToOrigin ? 'check' : 'uncheck';
log.debug('redirect to origin checkbox exists. Setting its state to', state);
await testSubjects.setEuiSwitch('returnToOriginModeSwitch', state);
}
}
public async libraryNotificationExists(panelTitle: string) {
log.debug('searching for library modal on panel:', panelTitle);
const panel = await testSubjects.find(
`embeddablePanelHeading-${panelTitle.replace(/ /g, '')}`
);
const libraryActionExists = await testSubjects.descendantExists(
'embeddablePanelNotification-ACTION_LIBRARY_NOTIFICATION',
panel
);
return libraryActionExists;
}
public async saveFromModal(

View file

@ -86,7 +86,7 @@ export const SaveModal = (props: Props) => {
savedObjectsTagging={savedObjectsTagging}
initialTags={tagsIds}
onSave={(saveProps) => {
const saveToLibrary = saveProps.dashboardId === null;
const saveToLibrary = Boolean(saveProps.addToLibrary);
onSave(saveProps, { saveToLibrary });
}}
onClose={onClose}

View file

@ -16,6 +16,7 @@ import { SavedObjectTaggingPluginStart } from '../../../saved_objects_tagging/pu
export type DashboardSaveProps = OnSaveProps & {
returnToOrigin: boolean;
dashboardId?: string | null;
addToLibrary?: boolean;
newTags?: string[];
};

View file

@ -145,7 +145,11 @@ export function getTopNavConfig({
const saveModalProps = {
onSave: async (
props: OnSaveProps & { returnToOrigin?: boolean; dashboardId?: string | null }
props: OnSaveProps & {
returnToOrigin?: boolean;
dashboardId?: string | null;
addToLibrary?: boolean;
}
) => {
try {
await checkForDuplicateTitle(
@ -172,7 +176,7 @@ export function getTopNavConfig({
await savedMap.save({
...props,
newTags: selectedTags,
saveByReference: !props.dashboardId,
saveByReference: Boolean(props.addToLibrary),
});
// showSaveModal wrapper requires onSave to return an object with an id to close the modal after successful save
return { id: 'id' };

View file

@ -23,28 +23,57 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
const security = getService('security');
const createNewLens = async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickVisType('lens');
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.configureDimension({
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
operation: 'avg',
field: 'bytes',
});
await PageObjects.lens.switchToVisualization('lnsMetric');
await PageObjects.lens.waitForVisualization();
await PageObjects.lens.assertMetric('Average of bytes', '5,727.322');
};
const createAndSaveDashboard = async (dashboardName: string) => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await dashboardAddPanel.clickOpenAddPanel();
await dashboardAddPanel.filterEmbeddableNames('lnsXYvis');
await find.clickByButtonText('lnsXYvis');
await dashboardAddPanel.closeAddPanel();
await PageObjects.lens.goToTimeRange();
await PageObjects.dashboard.saveDashboard(dashboardName);
await PageObjects.dashboard.gotoDashboardLandingPage();
await listingTable.searchAndExpectItemsCount('dashboard', dashboardName, 1);
};
const loadExistingLens = async () => {
await PageObjects.visualize.gotoVisualizationLandingPage();
await listingTable.searchForItemWithName('Artistpreviouslyknownaslens');
await PageObjects.lens.clickVisualizeListItemTitle('Artistpreviouslyknownaslens');
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.assertMetric('Maximum of bytes', '19,986');
};
describe('lens add-to-dashboards tests', () => {
it('should allow new lens vizs be added to a new dashboard', async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickVisType('lens');
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.configureDimension({
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
operation: 'avg',
field: 'bytes',
});
await PageObjects.lens.switchToVisualization('lnsMetric');
await PageObjects.lens.waitForVisualization();
await PageObjects.lens.assertMetric('Average of bytes', '5,727.322');
await PageObjects.lens.save('New Lens from Modal', false, false, 'new');
it('should allow new lens to be added by value to a new dashboard', async () => {
await createNewLens();
await PageObjects.lens.save('New Lens from Modal', false, false, false, 'new');
await PageObjects.dashboard.waitForRenderComplete();
await PageObjects.lens.assertMetric('Average of bytes', '5,727.322');
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
'New Lens from Modal'
);
expect(isLinked).to.be(false);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
@ -52,18 +81,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.timeToVisualize.resetNewDashboard();
});
it('should allow existing lens vizs be added to a new dashboard', async () => {
await PageObjects.visualize.gotoVisualizationLandingPage();
await listingTable.searchForItemWithName('Artistpreviouslyknownaslens');
await PageObjects.lens.clickVisualizeListItemTitle('Artistpreviouslyknownaslens');
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.assertMetric('Maximum of bytes', '19,986');
await PageObjects.lens.save('Artistpreviouslyknownaslens Copy', true, false, 'new');
it('should allow existing lens be added by value to a new dashboard', async () => {
await loadExistingLens();
await PageObjects.lens.save('Artistpreviouslyknownaslens Copy', true, false, false, 'new');
await PageObjects.dashboard.waitForRenderComplete();
await PageObjects.lens.assertMetric('Maximum of bytes', '19,986');
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
'Artistpreviouslyknownaslens Copy'
);
expect(isLinked).to.be(false);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
@ -71,38 +99,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.timeToVisualize.resetNewDashboard();
});
it('should allow new lens vizs be added to an existing dashboard', async () => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await dashboardAddPanel.clickOpenAddPanel();
await dashboardAddPanel.filterEmbeddableNames('lnsXYvis');
await find.clickByButtonText('lnsXYvis');
await dashboardAddPanel.closeAddPanel();
await PageObjects.lens.goToTimeRange();
await PageObjects.dashboard.saveDashboard('My Very Cool Dashboard');
await PageObjects.dashboard.gotoDashboardLandingPage();
await listingTable.searchAndExpectItemsCount('dashboard', 'My Very Cool Dashboard', 1);
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickVisType('lens');
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.configureDimension({
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
operation: 'avg',
field: 'bytes',
});
await PageObjects.lens.switchToVisualization('lnsMetric');
await PageObjects.lens.waitForVisualization();
await PageObjects.lens.assertMetric('Average of bytes', '5,727.322');
it('should allow new lens be added by value to an existing dashboard', async () => {
await createAndSaveDashboard('My Very Cool Dashboard');
await createNewLens();
await PageObjects.lens.save(
'New Lens from Modal',
false,
false,
false,
'existing',
'My Very Cool Dashboard'
);
@ -110,34 +115,24 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.dashboard.waitForRenderComplete();
await PageObjects.lens.assertMetric('Average of bytes', '5,727.322');
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
'New Lens from Modal'
);
expect(isLinked).to.be(false);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(2);
});
it('should allow existing lens vizs be added to an existing dashboard', async () => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await dashboardAddPanel.clickOpenAddPanel();
await dashboardAddPanel.filterEmbeddableNames('lnsXYvis');
await find.clickByButtonText('lnsXYvis');
await dashboardAddPanel.closeAddPanel();
await PageObjects.lens.goToTimeRange();
await PageObjects.dashboard.saveDashboard('My Wonderful Dashboard');
await PageObjects.dashboard.gotoDashboardLandingPage();
await listingTable.searchAndExpectItemsCount('dashboard', 'My Wonderful Dashboard', 1);
await PageObjects.visualize.gotoVisualizationLandingPage();
await listingTable.searchForItemWithName('Artistpreviouslyknownaslens');
await PageObjects.lens.clickVisualizeListItemTitle('Artistpreviouslyknownaslens');
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.assertMetric('Maximum of bytes', '19,986');
it('should allow existing lens be added by value to an existing dashboard', async () => {
await createAndSaveDashboard('My Wonderful Dashboard');
await loadExistingLens();
await PageObjects.lens.save(
'Artistpreviouslyknownaslens Copy',
true,
false,
false,
'existing',
'My Wonderful Dashboard'
);
@ -145,6 +140,96 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.dashboard.waitForRenderComplete();
await PageObjects.lens.assertMetric('Maximum of bytes', '19,986');
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
'Artistpreviouslyknownaslens Copy'
);
expect(isLinked).to.be(false);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(2);
});
it('should allow new lens to be added by reference to a new dashboard', async () => {
await createNewLens();
await PageObjects.lens.save('New by ref Lens from Modal', false, false, true, 'new');
await PageObjects.dashboard.waitForRenderComplete();
await PageObjects.lens.assertMetric('Average of bytes', '5,727.322');
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
'New by ref Lens from Modal'
);
expect(isLinked).to.be(true);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
await PageObjects.timeToVisualize.resetNewDashboard();
});
it('should allow existing lens be added by reference to a new dashboard', async () => {
await loadExistingLens();
await PageObjects.lens.save('Artistpreviouslyknownaslens by ref', true, false, true, 'new');
await PageObjects.dashboard.waitForRenderComplete();
await PageObjects.lens.assertMetric('Maximum of bytes', '19,986');
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
'Artistpreviouslyknownaslens by ref'
);
expect(isLinked).to.be(true);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
await PageObjects.timeToVisualize.resetNewDashboard();
});
it('should allow new lens be added by reference to an existing dashboard', async () => {
await createAndSaveDashboard('My Very Cool Dashboard 2');
await createNewLens();
await PageObjects.lens.save(
'New Lens by ref from Modal',
false,
false,
true,
'existing',
'My Very Cool Dashboard 2'
);
await PageObjects.dashboard.waitForRenderComplete();
await PageObjects.lens.assertMetric('Average of bytes', '5,727.322');
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
'New Lens by ref from Modal'
);
expect(isLinked).to.be(true);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(2);
});
it('should allow existing lens be added by reference to an existing dashboard', async () => {
await createAndSaveDashboard('My Wonderful Dashboard 2');
await loadExistingLens();
await PageObjects.lens.save(
'Artistpreviouslyknownaslens by ref 2',
true,
false,
true,
'existing',
'My Wonderful Dashboard 2'
);
await PageObjects.dashboard.waitForRenderComplete();
await PageObjects.lens.assertMetric('Maximum of bytes', '19,986');
const isLinked = await PageObjects.timeToVisualize.libraryNotificationExists(
'Artistpreviouslyknownaslens by ref 2'
);
expect(isLinked).to.be(true);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(2);

View file

@ -39,27 +39,33 @@ export default function ({ getPageObjects, getService }) {
await security.testUser.restoreDefaults();
});
it('should allow new map be added to a new dashboard', async () => {
it('should allow new map be added by value to a new dashboard', async () => {
await PageObjects.maps.openNewMap();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.maps.waitForLayersToLoad();
await testSubjects.click('mapSaveButton');
await PageObjects.timeToVisualize.saveFromModal('map 1', { addToDashboard: 'new' });
await PageObjects.timeToVisualize.saveFromModal('map 1', {
addToDashboard: 'new',
saveToLibrary: false,
});
await PageObjects.dashboard.waitForRenderComplete();
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
const isInLibrary = await PageObjects.timeToVisualize.libraryNotificationExists('map 1');
expect(isInLibrary).to.be(false);
await PageObjects.timeToVisualize.resetNewDashboard();
});
it('should allow existing maps be added to a new dashboard', async () => {
it('should allow existing maps be added by value to a new dashboard', async () => {
await PageObjects.maps.loadSavedMap('document example');
await testSubjects.click('mapSaveButton');
await PageObjects.timeToVisualize.saveFromModal('document example copy', {
saveToLibrary: false,
addToDashboard: 'new',
saveAsNew: true,
});
@ -69,10 +75,14 @@ export default function ({ getPageObjects, getService }) {
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
const isInLibrary = await PageObjects.timeToVisualize.libraryNotificationExists(
'document example copy'
);
expect(isInLibrary).to.be(false);
await PageObjects.timeToVisualize.resetNewDashboard();
});
it('should allow new map be added to an existing dashboard', async () => {
it('should allow new map be added by value to an existing dashboard', async () => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
@ -86,6 +96,7 @@ export default function ({ getPageObjects, getService }) {
await testSubjects.click('mapSaveButton');
await PageObjects.timeToVisualize.saveFromModal('My New Map 2', {
saveToLibrary: false,
addToDashboard: 'existing',
dashboardId: 'My Very Cool Dashboard',
});
@ -94,9 +105,14 @@ export default function ({ getPageObjects, getService }) {
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
const isInLibrary = await PageObjects.timeToVisualize.libraryNotificationExists(
'My New Map 2'
);
expect(isInLibrary).to.be(false);
});
it('should allow existing maps be added to an existing dashboard', async () => {
it('should allow existing maps be added by value to an existing dashboard', async () => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
@ -108,6 +124,7 @@ export default function ({ getPageObjects, getService }) {
await testSubjects.click('mapSaveButton');
await PageObjects.timeToVisualize.saveFromModal('document example copy 2', {
saveToLibrary: false,
addToDashboard: 'existing',
dashboardId: 'My Wonderful Dashboard',
saveAsNew: true,
@ -117,6 +134,113 @@ export default function ({ getPageObjects, getService }) {
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
const isInLibrary = await PageObjects.timeToVisualize.libraryNotificationExists(
'document example copy 2'
);
expect(isInLibrary).to.be(false);
});
it('should allow new map be added by reference to a new dashboard', async () => {
await PageObjects.maps.openNewMap();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.maps.waitForLayersToLoad();
await testSubjects.click('mapSaveButton');
await PageObjects.timeToVisualize.saveFromModal('map 1', {
addToDashboard: 'new',
saveToLibrary: true,
});
await PageObjects.dashboard.waitForRenderComplete();
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
const isInLibrary = await PageObjects.timeToVisualize.libraryNotificationExists('map 1');
expect(isInLibrary).to.be(true);
await PageObjects.timeToVisualize.resetNewDashboard();
});
it('should allow existing maps be added by reference to a new dashboard', async () => {
await PageObjects.maps.loadSavedMap('document example');
await testSubjects.click('mapSaveButton');
await PageObjects.timeToVisualize.saveFromModal('document example copy', {
saveToLibrary: true,
addToDashboard: 'new',
saveAsNew: true,
});
await PageObjects.dashboard.waitForRenderComplete();
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
const isInLibrary = await PageObjects.timeToVisualize.libraryNotificationExists(
'document example copy'
);
expect(isInLibrary).to.be(true);
await PageObjects.timeToVisualize.resetNewDashboard();
});
it('should allow new map be added by reference to an existing dashboard', async () => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.saveDashboard('My Super Cool Dashboard');
await PageObjects.dashboard.gotoDashboardLandingPage();
await listingTable.searchAndExpectItemsCount('dashboard', 'My Super Cool Dashboard', 1);
await PageObjects.maps.openNewMap();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.maps.waitForLayersToLoad();
await testSubjects.click('mapSaveButton');
await PageObjects.timeToVisualize.saveFromModal('My New Map 2', {
saveToLibrary: true,
addToDashboard: 'existing',
dashboardId: 'My Super Cool Dashboard',
});
await PageObjects.dashboard.waitForRenderComplete();
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
const isInLibrary = await PageObjects.timeToVisualize.libraryNotificationExists(
'My New Map 2'
);
expect(isInLibrary).to.be(true);
});
it('should allow existing maps be added by reference to an existing dashboard', async () => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.saveDashboard('My Amazing Dashboard');
await PageObjects.dashboard.gotoDashboardLandingPage();
await listingTable.searchAndExpectItemsCount('dashboard', 'My Amazing Dashboard', 1);
await PageObjects.maps.loadSavedMap('document example');
await testSubjects.click('mapSaveButton');
await PageObjects.timeToVisualize.saveFromModal('document example copy 2', {
saveToLibrary: true,
addToDashboard: 'existing',
dashboardId: 'My Amazing Dashboard',
saveAsNew: true,
});
await PageObjects.dashboard.waitForRenderComplete();
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.eql(1);
const isInLibrary = await PageObjects.timeToVisualize.libraryNotificationExists(
'document example copy 2'
);
expect(isInLibrary).to.be(true);
});
});
}

View file

@ -376,6 +376,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
title: string,
saveAsNew?: boolean,
redirectToOrigin?: boolean,
saveToLibrary?: boolean,
addToDashboard?: 'new' | 'existing' | null,
dashboardId?: string
) {
@ -387,6 +388,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
redirectToOrigin,
addToDashboard: addToDashboard ? addToDashboard : null,
dashboardId,
saveToLibrary,
});
await testSubjects.click('confirmSaveSavedObjectButton');