diff --git a/changelogs/fragments/9145.yml b/changelogs/fragments/9145.yml new file mode 100644 index 000000000000..e2c8944d16c8 --- /dev/null +++ b/changelogs/fragments/9145.yml @@ -0,0 +1,2 @@ +feat: +- Add a "Learn More" flyout providing additional information to the collaborators page. ([#9145](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/9145)) \ No newline at end of file diff --git a/changelogs/fragments/9159.yml b/changelogs/fragments/9159.yml new file mode 100644 index 000000000000..8bb438a4c5b0 --- /dev/null +++ b/changelogs/fragments/9159.yml @@ -0,0 +1,2 @@ +fix: +- Fix Unhandled Error Response of dev tool with MDS ([#9159](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/9159)) \ No newline at end of file diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index ac299977d911..1f68142f243a 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -437,8 +437,10 @@ export class DocLinksService { advancedSettings: `${OPENSEARCH_DASHBOARDS_VERSIONED_DOCS}management/advanced-settings/`, }, workspace: { - // https://opensearch.org/docs/latest/dashboards/workspace/workspace-acl/ - acl: `${OPENSEARCH_DASHBOARDS_VERSIONED_DOCS}workspace/workspace-acl/`, + // https://opensearch.org/docs/latest/dashboards/workspace/workspace-acl/#defining-workspace-collaborators + collaborators: `${OPENSEARCH_DASHBOARDS_VERSIONED_DOCS}workspace/workspace-acl/#defining-workspace-collaborators`, + // https://opensearch.org/docs/latest/dashboards/workspace/workspace-acl/#workspace-privacy + privacy: `${OPENSEARCH_DASHBOARDS_VERSIONED_DOCS}workspace/workspace-acl/#workspace-privacy`, }, }, noDocumentation: { @@ -854,6 +856,7 @@ export interface DocLinksStart { readonly visualize: Record; readonly dashboards: Record; readonly management: Record; + readonly workspace: Record; }; readonly noDocumentation: { readonly auditbeat: string; diff --git a/src/plugins/advanced_settings/public/management_app/__snapshots__/advanced_settings.test.tsx.snap b/src/plugins/advanced_settings/public/management_app/__snapshots__/advanced_settings.test.tsx.snap index 2bfa367a4bff..6d496ae9d7c5 100644 --- a/src/plugins/advanced_settings/public/management_app/__snapshots__/advanced_settings.test.tsx.snap +++ b/src/plugins/advanced_settings/public/management_app/__snapshots__/advanced_settings.test.tsx.snap @@ -419,7 +419,8 @@ exports[`AdvancedSettings should render normally when use updated UX 1`] = ` "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, } @@ -1077,7 +1078,8 @@ exports[`AdvancedSettings should render normally when use updated UX 1`] = ` "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, } @@ -2014,7 +2016,8 @@ exports[`AdvancedSettings should render normally when use updated UX 1`] = ` "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, } @@ -2429,7 +2432,8 @@ exports[`AdvancedSettings should render normally when use updated UX 1`] = ` "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, } @@ -2844,7 +2848,8 @@ exports[`AdvancedSettings should render normally when use updated UX 1`] = ` "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, } @@ -3259,7 +3264,8 @@ exports[`AdvancedSettings should render normally when use updated UX 1`] = ` "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, } @@ -3729,7 +3735,8 @@ exports[`AdvancedSettings should render normally when use updated UX 1`] = ` "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, } @@ -4146,7 +4153,8 @@ exports[`AdvancedSettings should render normally when use updated UX 1`] = ` "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, } diff --git a/src/plugins/console/server/routes/api/console/proxy/create_handler.ts b/src/plugins/console/server/routes/api/console/proxy/create_handler.ts index f2155011cd74..90e268154d23 100644 --- a/src/plugins/console/server/routes/api/console/proxy/create_handler.ts +++ b/src/plugins/console/server/routes/api/console/proxy/create_handler.ts @@ -32,7 +32,6 @@ import { OpenSearchDashboardsRequest, RequestHandler } from 'opensearch-dashboar import { trimStart } from 'lodash'; import { Readable } from 'stream'; import { stringify } from '@osd/std'; -import { ApiResponse } from '@opensearch-project/opensearch'; // eslint-disable-next-line @osd/eslint/no-restricted-paths import { ensureRawRequest } from '../../../../../../../core/server/http/router'; @@ -88,10 +87,6 @@ export const createHandler = ({ }: RouteDependencies): RequestHandler => async (ctx, request, response) => { const { body, query } = request; const { path, method, dataSourceId } = query; - const client = dataSourceId - ? await ctx.dataSource.opensearch.getClient(dataSourceId) - : ctx.core.opensearch.client.asCurrentUserWithLongNumeralsSupport; - let opensearchResponse: ApiResponse; if (!pathFilters.some((re) => re.test(path))) { return response.forbidden({ @@ -103,6 +98,10 @@ export const createHandler = ({ } try { + const client = dataSourceId + ? await ctx.dataSource.opensearch.getClient(dataSourceId) + : ctx.core.opensearch.client.asCurrentUserWithLongNumeralsSupport; + // TODO: proxy header will fail sigv4 auth type in data source, need create issue in opensearch-js repo to track const requestHeaders = dataSourceId ? {} @@ -111,7 +110,7 @@ export const createHandler = ({ }; const bufferedBody = await buildBufferedBody(body); - opensearchResponse = await client.transport.request( + const opensearchResponse = await client.transport.request( { path: toUrlPath(path), method, body: bufferedBody }, { headers: requestHeaders } ); @@ -148,7 +147,7 @@ export const createHandler = ({ }); } catch (e: any) { const isResponseErrorFlag = isResponseError(e); - if (!isResponseError) log.error(e); + if (!isResponseErrorFlag) log.error(e); const errorMessage = isResponseErrorFlag ? stringify(e.meta.body) : e.message; // core http route handler has special logic that asks for stream readable input to pass error opaquely const errorResponseBody = new Readable({ @@ -158,7 +157,7 @@ export const createHandler = ({ }, }); return response.customError({ - statusCode: isResponseErrorFlag ? e.statusCode : 502, + statusCode: e.statusCode || 502, body: errorResponseBody, headers: { 'Content-Type': 'application/json', diff --git a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap index 3bf47018e304..303a9238a2af 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap @@ -796,7 +796,8 @@ exports[`Dashboard top nav render in embed mode 1`] = ` "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, }, @@ -1881,7 +1882,8 @@ exports[`Dashboard top nav render in embed mode, and force hide filter bar 1`] = "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, }, @@ -2966,7 +2968,8 @@ exports[`Dashboard top nav render in embed mode, components can be forced show b "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, }, @@ -4051,7 +4054,8 @@ exports[`Dashboard top nav render in full screen mode with appended URL param bu "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, }, @@ -5136,7 +5140,8 @@ exports[`Dashboard top nav render in full screen mode, no componenets should be "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, }, @@ -6221,7 +6226,8 @@ exports[`Dashboard top nav render with all components 1`] = ` "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, }, diff --git a/src/plugins/data/public/ui/query_editor/__snapshots__/language_selector.test.tsx.snap b/src/plugins/data/public/ui/query_editor/__snapshots__/language_selector.test.tsx.snap index be80b5be7182..2a4958573d74 100644 --- a/src/plugins/data/public/ui/query_editor/__snapshots__/language_selector.test.tsx.snap +++ b/src/plugins/data/public/ui/query_editor/__snapshots__/language_selector.test.tsx.snap @@ -562,7 +562,8 @@ exports[`LanguageSelector should select DQL if language is kuery 1`] = ` "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, }, @@ -1235,7 +1236,8 @@ exports[`LanguageSelector should select lucene if language is lucene 1`] = ` "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/visualize/viz-index/", }, "workspace": Object { - "acl": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/", + "collaborators": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#defining-workspace-collaborators", + "privacy": "https://opensearch.org/docs/mocked-test-branch/dashboards/workspace/workspace-acl/#workspace-privacy", }, }, }, diff --git a/src/plugins/workspace/public/components/workspace_collaborators/workspace_collaborators.tsx b/src/plugins/workspace/public/components/workspace_collaborators/workspace_collaborators.tsx index 7c06ae1a654d..aa3f9f74c5df 100644 --- a/src/plugins/workspace/public/components/workspace_collaborators/workspace_collaborators.tsx +++ b/src/plugins/workspace/public/components/workspace_collaborators/workspace_collaborators.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React from 'react'; +import React, { useState } from 'react'; import { EuiPage, EuiPanel, EuiSpacer } from '@elastic/eui'; import { i18n } from '@osd/i18n'; @@ -14,6 +14,7 @@ import { CoreStart } from '../../../../../core/public'; import { NavigationPublicPluginStart, TopNavControlDescriptionData, + TopNavControlLinkData, } from '../../../../navigation/public'; import { WorkspacePermissionSetting } from '../workspace_form/types'; import { WorkspaceCollaboratorTable } from '../workspace_form/workspace_collaborator_table'; @@ -25,6 +26,7 @@ import { } from '../workspace_form'; import { WorkspaceAttributeWithPermission } from '../../../../../core/types'; import { WorkspaceClient } from '../../workspace_client'; +import { WorkspacePrivacyFlyout } from '../workspace_form/workspace_privacy_flyout'; import { WorkspaceCollaboratorPrivacySettingPanel } from '../workspace_form/workspace_collaborator_privacy_setting_panel'; export const WorkspaceCollaborators = () => { @@ -43,6 +45,8 @@ export const WorkspaceCollaborators = () => { collaboratorTypes: WorkspaceCollaboratorTypesService; workspaceClient: WorkspaceClient; }>(); + + const [isPrivacyFlyoutVisible, setIsPrivacyFlyoutVisible] = useState(false); const displayedCollaboratorTypes = useObservable(collaboratorTypes.getTypes$()) ?? []; const currentWorkspace = useObservable( @@ -90,6 +94,10 @@ export const WorkspaceCollaborators = () => { if (!currentWorkspace || !isPermissionEnabled) { return null; } + + const handleLearnMoreClick = (targetElement: HTMLElement) => { + setIsPrivacyFlyoutVisible((value) => !value); + }; return ( { description: i18n.translate('workspace.collaborators.description', { defaultMessage: 'Manage workspace access and permissions.', }), + links: { + label: i18n.translate('workspace.form.panels.collaborator.learnMore', { + defaultMessage: 'Learn more', + }), + controlType: 'link', + run: handleLearnMoreClick, + } as TopNavControlLinkData, } as TopNavControlDescriptionData, ]} setMountPoint={application.setAppDescriptionControls} @@ -130,6 +145,9 @@ export const WorkspaceCollaborators = () => { /> + {isPrivacyFlyoutVisible && ( + setIsPrivacyFlyoutVisible(false)} /> + )} ); }; diff --git a/src/plugins/workspace/public/components/workspace_form/__snapshots__/workspace_privacy_flyout.test.tsx.snap b/src/plugins/workspace/public/components/workspace_form/__snapshots__/workspace_privacy_flyout.test.tsx.snap new file mode 100644 index 000000000000..67e6eda75b03 --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_form/__snapshots__/workspace_privacy_flyout.test.tsx.snap @@ -0,0 +1,412 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`WorkspacePrivacyFlyout should render normally 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
+
+ +
+
+
+
+
+ Workspace privacy is the default access level configured for all. Collaborators will have permissions based on their individual level access and user group participation. Workspace permissions are resolved by the least restrictive option. + +For example, if workspace privacy is set to "Anyone can edit", any collaborator with "Read only" permission will also be able to edit the workspace assets as if their permission is "Read and write", since "Anyone can edit" is the least restrictive permission. + +Similarly, if a workspace collaborator is added with individual permissions, but they belong to a group with less restrictive access, the least restrictive access permission will take precedence. + +For example, if "userA' is added as a collaborator with "read and write" permissions, but they belong to "userGroupB", which has "Admin" permissions, the permissions granted to "userGroupB" will take precedence. +
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+ Workspace collaborators include individual users and user groups who have been granted permission to access and interact with the workspace. The collaborators can have Read-only, Read and write, and Admin permission levels within the workspace. Access level to the workspace is defined by each collaborator's individual permission level, their group participation access level, and workspace privacy level. The least restrictive access permission will take precedence. +
+
+ +
+
+
+
+
+
+
+
+
+ + , + "container":
+
+ +
+
+
+
+
+ Workspace privacy is the default access level configured for all. Collaborators will have permissions based on their individual level access and user group participation. Workspace permissions are resolved by the least restrictive option. + +For example, if workspace privacy is set to "Anyone can edit", any collaborator with "Read only" permission will also be able to edit the workspace assets as if their permission is "Read and write", since "Anyone can edit" is the least restrictive permission. + +Similarly, if a workspace collaborator is added with individual permissions, but they belong to a group with less restrictive access, the least restrictive access permission will take precedence. + +For example, if "userA' is added as a collaborator with "read and write" permissions, but they belong to "userGroupB", which has "Admin" permissions, the permissions granted to "userGroupB" will take precedence. +
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+ Workspace collaborators include individual users and user groups who have been granted permission to access and interact with the workspace. The collaborators can have Read-only, Read and write, and Admin permission levels within the workspace. Access level to the workspace is defined by each collaborator's individual permission level, their group participation access level, and workspace privacy level. The least restrictive access permission will take precedence. +
+
+ +
+
+
+
+
+
+
+
+
+ , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/src/plugins/workspace/public/components/workspace_form/workspace_privacy_flyout.test.tsx b/src/plugins/workspace/public/components/workspace_form/workspace_privacy_flyout.test.tsx new file mode 100644 index 000000000000..3f72bd6c12d0 --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_form/workspace_privacy_flyout.test.tsx @@ -0,0 +1,23 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +import React from 'react'; +import { render } from '@testing-library/react'; +import { WorkspacePrivacyFlyout } from './workspace_privacy_flyout'; + +const setup = () => { + const handleCloseMock = jest.fn(); + const renderResult = render(); + return { + renderResult, + handleCloseMock, + }; +}; + +describe('WorkspacePrivacyFlyout', () => { + it('should render normally', () => { + const { renderResult } = setup(); + expect(renderResult).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/workspace/public/components/workspace_form/workspace_privacy_flyout.tsx b/src/plugins/workspace/public/components/workspace_form/workspace_privacy_flyout.tsx new file mode 100644 index 000000000000..dbc6e87be806 --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_form/workspace_privacy_flyout.tsx @@ -0,0 +1,103 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { i18n } from '@osd/i18n'; +import { + EuiFlyout, + EuiFlyoutHeader, + EuiTitle, + EuiFlyoutBody, + EuiAccordion, + EuiSpacer, + EuiText, + EuiLink, +} from '@elastic/eui'; +import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; + +const WORKSPACE_PRIVACY_FLYOUT_TITLE_ID = 'workspacePrivacyFlyoutTitle'; + +export interface WorkspacePrivacyFlyoutProps { + onClose: () => void; +} + +export const WorkspacePrivacyFlyout = ({ onClose }: WorkspacePrivacyFlyoutProps) => { + const { + services: { docLinks }, + } = useOpenSearchDashboards(); + + const WorkspacePrivacyFlyoutItems = [ + { + id: 'workspacePrivacy', + title: i18n.translate('workspace.forms.privacyFlyout.workspacePrivacy.title', { + defaultMessage: 'Workspace privacy', + }), + description: i18n.translate('workspace.forms.privacyFlyout.workspacePrivacy.description', { + defaultMessage: `Workspace privacy is the default access level configured for all. Collaborators will have permissions based on their individual level access and user group participation. Workspace permissions are resolved by the least restrictive option.\n\nFor example, if workspace privacy is set to "Anyone can edit", any collaborator with "Read only" permission will also be able to edit the workspace assets as if their permission is "Read and write", since "Anyone can edit" is the least restrictive permission.\n\nSimilarly, if a workspace collaborator is added with individual permissions, but they belong to a group with less restrictive access, the least restrictive access permission will take precedence.\n\nFor example, if "userA' is added as a collaborator with "read and write" permissions, but they belong to "userGroupB", which has "Admin" permissions, the permissions granted to "userGroupB" will take precedence.`, + }), + linkText: i18n.translate('workspace.forms.privacyFlyout.workspacePrivacy.linkText', { + defaultMessage: 'Learn more in documentation', + }), + link: docLinks?.links.opensearchDashboards.workspace.privacy, + }, + { + id: 'collaborators', + title: i18n.translate('workspace.forms.privacyFlyout.collaborator.title', { + defaultMessage: 'Collaborators', + }), + description: i18n.translate('workspace.forms.privacyFlyout.collaborator.description', { + defaultMessage: `Workspace collaborators include individual users and user groups who have been granted permission to access and interact with the workspace. The collaborators can have Read-only, Read and write, and Admin permission levels within the workspace. Access level to the workspace is defined by each collaborator's individual permission level, their group participation access level, and workspace privacy level. The least restrictive access permission will take precedence.`, + }), + linkText: i18n.translate('workspace.forms.privacyFlyout.collaborator.linkText', { + defaultMessage: 'Learn more in documentation', + }), + link: docLinks?.links.opensearchDashboards.workspace.collaborators, + }, + ]; + return ( + + + +

+ {i18n.translate('workspace.forms.privacyFlyout.title', { + defaultMessage: 'Workspace access', + })} +

+
+
+ + {WorkspacePrivacyFlyoutItems.map(({ id, title, description, linkText, link }) => ( + <> + +

{title}

+ + } + paddingSize="l" + initialIsOpen={true} + > + + {description} + + + + {linkText} + +
+ + + ))} +
+
+ ); +};