Skip to content

Commit

Permalink
[Enterprise Search] Convert Role mappings for both apps to use flyouts (
Browse files Browse the repository at this point in the history
#101198) (#101425)

* Add RoleOptionLabel component

* Refactor RoleSelector to use EuiRadioGroup

Previously, we used individual radio buttons in a map in the component. However the new designs have a shared label and work best in the EuiRadioGroup component.

* Add reducer and actions to logic file for flyout visibility

* Remove redirects in favor of refreshing lists

With the existing multi-page view, we redirect after creating, editing or deleting a mapping. We now simply refresh the list after the action.

Also as a part of this commit, we show a hard-coded error message if a user tries to navigate to a non-existant mapping, instead of redirecting to a 404 page

* Add RoleMappingFlyout component

* Refactor AttributeSelector

No longer uses a panel or has the need for flex groups

- Also added a test for 100% coverage

* Refactor RoleMappingsTable

- Use EuiButtonIcons instead of Link
- Manage button now triggers flyout instead of linking to route

* Remove AddRoleMappingButton

We can just use an EuiButton to trigger the flyout

* Convert to use RoleSelector syntax

- Passes the entire array to the component instead of mapping.
- Uses ‘id’ instead of ‘type’ to match EUI component
- For App Search, as per design and PM direction, dropping labels for advanced and standard roles and showing them all in the same list.
- Removed unused constant and i18ns

* Move constants to shared

Will do a lot more of this in a future PR

* Remove DeleteMappingCallout

- This now an action in the row
- Also added tests for correct titles for 100% test coverage

* Remove routers and routes

- SPA FTW

* No longer pass isNew as prop

- Determine based on existence of Role Mapping instead

* No longer need to initialze role mapping in the component

This will become a flyout and the intialization will be triggered when the button in the table is clicked.

* Remove flash messages

This will be handled globally in the main component.

* Wrap components with flyout

Also add to main RoleMappings views

* Add form row validation for App Search

* Remove unnecessary layout components

- Don’t need the panel, headings, spacer, and Flex components
- Also removed constants and i18n from unused headings

* Wire up handleDeleteMapping to take ID param

The method now passes the ID directly from the table row action item

* Add EuiPortal wrapper for flyout

Without this, the flyout was was under the overlay. Hide whitespace changes on this commit

* Add spacer to better match design

* Update constants for new copy from design

* Replace all engines/groups radio and group/engine selectors

- The designs call for a radio group and a combo box, instead of separate radios and a list of checkboxes
- Also added a spacer to each layout

* Remove util that is no longer needed

- This was used for generating routes that are no longer there
- Also removed unused test file from a component deleted in an earlier PR
- Fix test since spacer was added

* Add missing i18n constant

* Add back missing scoped engine check

* Rename roleId -> roleMappingId

* Use shared constant for “Cancel”

Co-authored-by: Kibana Machine <[email protected]>

Co-authored-by: Scotty Bollinger <[email protected]>
  • Loading branch information
kibanamachine and scottybollinger authored Jun 4, 2021
1 parent d34b639 commit a086f26
Show file tree
Hide file tree
Showing 47 changed files with 1,043 additions and 1,020 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,6 @@ import { i18n } from '@kbn/i18n';

import { AdvanceRoleType } from '../../types';

export const SAVE_ROLE_MAPPING = i18n.translate(
'xpack.enterpriseSearch.appSearch.roleMapping.saveRoleMappingButtonLabel',
{ defaultMessage: 'Save role mapping' }
);
export const UPDATE_ROLE_MAPPING = i18n.translate(
'xpack.enterpriseSearch.appSearch.roleMapping.updateRoleMappingButtonLabel',
{ defaultMessage: 'Update role mapping' }
);

export const EMPTY_ROLE_MAPPINGS_BODY = i18n.translate(
'xpack.enterpriseSearch.appSearch.roleMapping.emptyRoleMappingsBody',
{
Expand Down Expand Up @@ -126,74 +117,71 @@ export const ADMIN_ROLE_TYPE_DESCRIPTION = i18n.translate(
}
);

export const ADVANCED_ROLE_SELECTORS_TITLE = i18n.translate(
'xpack.enterpriseSearch.appSearch.advancedRoleSelectorsTitle',
export const ENGINE_REQUIRED_ERROR = i18n.translate(
'xpack.enterpriseSearch.appSearch.engineRequiredError',
{
defaultMessage: 'Full or limited engine access',
defaultMessage: 'At least one assigned engine is required.',
}
);

export const ROLE_TITLE = i18n.translate('xpack.enterpriseSearch.appSearch.roleTitle', {
defaultMessage: 'Role',
});

export const FULL_ENGINE_ACCESS_TITLE = i18n.translate(
'xpack.enterpriseSearch.appSearch.fullEngineAccessTitle',
export const ALL_ENGINES_LABEL = i18n.translate(
'xpack.enterpriseSearch.appSearch.allEnginesLabel',
{
defaultMessage: 'Full engine access',
defaultMessage: 'Assign to all engines',
}
);

export const FULL_ENGINE_ACCESS_DESCRIPTION = i18n.translate(
'xpack.enterpriseSearch.appSearch.fullEngineAccessDescription',
export const ALL_ENGINES_DESCRIPTION = i18n.translate(
'xpack.enterpriseSearch.appSearch.allEnginesDescription',
{
defaultMessage: 'Access to all current and future engines.',
defaultMessage:
'Assigning to all engines includes all current and future engines as created and administered at a later date.',
}
);

export const LIMITED_ENGINE_ACCESS_TITLE = i18n.translate(
'xpack.enterpriseSearch.appSearch.limitedEngineAccessTitle',
export const SPECIFIC_ENGINES_LABEL = i18n.translate(
'xpack.enterpriseSearch.appSearch.specificEnginesLabel',
{
defaultMessage: 'Limited engine access',
defaultMessage: 'Assign to specific engines',
}
);

export const LIMITED_ENGINE_ACCESS_DESCRIPTION = i18n.translate(
'xpack.enterpriseSearch.appSearch.limitedEngineAccessDescription',
export const SPECIFIC_ENGINES_DESCRIPTION = i18n.translate(
'xpack.enterpriseSearch.appSearch.specificEnginesDescription',
{
defaultMessage: 'Limit user access to specific engines:',
defaultMessage: 'Assign to a select set of engines statically.',
}
);

export const ENGINE_ACCESS_TITLE = i18n.translate(
'xpack.enterpriseSearch.appSearch.engineAccessTitle',
export const ENGINE_ASSIGNMENT_LABEL = i18n.translate(
'xpack.enterpriseSearch.appSearch.engineAssignmentLabel',
{
defaultMessage: 'Engine access',
defaultMessage: 'Engine assignment',
}
);

export const ADVANCED_ROLE_TYPES = [
{
type: 'dev',
id: 'dev',
description: DEV_ROLE_TYPE_DESCRIPTION,
},
{
type: 'editor',
id: 'editor',
description: EDITOR_ROLE_TYPE_DESCRIPTION,
},
{
type: 'analyst',
id: 'analyst',
description: ANALYST_ROLE_TYPE_DESCRIPTION,
},
] as AdvanceRoleType[];

export const STANDARD_ROLE_TYPES = [
{
type: 'owner',
id: 'owner',
description: OWNER_ROLE_TYPE_DESCRIPTION,
},
{
type: 'admin',
id: 'admin',
description: ADMIN_ROLE_TYPE_DESCRIPTION,
},
] as AdvanceRoleType[];
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
* 2.0.
*/

export { RoleMappingsRouter } from './role_mappings_router';
export { RoleMappings } from './role_mappings';
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,16 @@ import { engines } from '../../__mocks__/engines.mock';

import React from 'react';

import { waitFor } from '@testing-library/dom';
import { shallow } from 'enzyme';

import { EuiCheckbox } from '@elastic/eui';
import { EuiComboBox, EuiComboBoxOptionOption, EuiRadioGroup } from '@elastic/eui';

import { Loading } from '../../../shared/loading';
import {
AttributeSelector,
DeleteMappingCallout,
RoleSelector,
} from '../../../shared/role_mapping';
import { AttributeSelector, RoleSelector } from '../../../shared/role_mapping';
import { asRoleMapping } from '../../../shared/role_mapping/__mocks__/roles';

import { STANDARD_ROLE_TYPES } from './constants';

import { RoleMapping } from './role_mapping';

describe('RoleMapping', () => {
Expand Down Expand Up @@ -68,39 +66,44 @@ describe('RoleMapping', () => {
});

it('renders', () => {
setMockValues({ ...mockValues, roleMapping: asRoleMapping });
const wrapper = shallow(<RoleMapping />);

expect(wrapper.find(AttributeSelector)).toHaveLength(1);
expect(wrapper.find(RoleSelector)).toHaveLength(5);
expect(wrapper.find(RoleSelector)).toHaveLength(1);
});

it('returns Loading when loading', () => {
setMockValues({ ...mockValues, dataLoading: true });
it('only passes standard role options for non-advanced roles', () => {
setMockValues({ ...mockValues, hasAdvancedRoles: false });
const wrapper = shallow(<RoleMapping />);

expect(wrapper.find(Loading)).toHaveLength(1);
expect(wrapper.find(RoleSelector).prop('roleOptions')).toHaveLength(STANDARD_ROLE_TYPES.length);
});

it('renders DeleteMappingCallout for existing mapping', () => {
setMockValues({ ...mockValues, roleMapping: asRoleMapping });
it('sets initial selected state when accessAllEngines is true', () => {
setMockValues({ ...mockValues, accessAllEngines: true });
const wrapper = shallow(<RoleMapping />);

expect(wrapper.find(DeleteMappingCallout)).toHaveLength(1);
expect(wrapper.find(EuiRadioGroup).prop('idSelected')).toBe('all');
});

it('hides DeleteMappingCallout for new mapping', () => {
const wrapper = shallow(<RoleMapping isNew />);
it('handles all/specific engines radio change', () => {
const wrapper = shallow(<RoleMapping />);
const radio = wrapper.find(EuiRadioGroup);
radio.simulate('change', { target: { checked: false } });

expect(wrapper.find(DeleteMappingCallout)).toHaveLength(0);
expect(actions.handleAccessAllEnginesChange).toHaveBeenCalledWith(false);
});

it('handles engine checkbox click', () => {
it('handles engine checkbox click', async () => {
const wrapper = shallow(<RoleMapping />);
wrapper
.find(EuiCheckbox)
.first()
.simulate('change', { target: { checked: true } });

expect(actions.handleEngineSelectionChange).toHaveBeenCalledWith(engines[0].name, true);
await waitFor(() =>
((wrapper.find(EuiComboBox).props() as unknown) as {
onChange: (a: EuiComboBoxOptionOption[]) => void;
}).onChange([{ label: engines[0].name, value: engines[0].name }])
);
wrapper.update();

expect(actions.handleEngineSelectionChange).toHaveBeenCalledWith([engines[0].name]);
});
});
Loading

0 comments on commit a086f26

Please sign in to comment.