Skip to content

Commit c1924c3

Browse files
[Enterprise Search] Refactor Role mappings landing pages for both products (#101534)
* Add constants and type * Add RoleMappingsHeading component I toyed with trying to make a shared component between the Role mappings and Users sections since they both have the same layout, but the need to have all of the conditional copy and button text just seemed too messy, so I opted to share this component between the two products and will make a UsersHeading component in a future PR * Remove action from table This is now in the RoleMappingsHeading component from the previous commit * Remove empty states Also removed the add mapping button since it is in the heading component * Remove page headings in favor of table headings * Remove a bunch of constants and translations * Update placeholder to match mockup
1 parent d920682 commit c1924c3

14 files changed

Lines changed: 158 additions & 179 deletions

File tree

x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/constants.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,6 @@ import { i18n } from '@kbn/i18n';
99

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

12-
export const EMPTY_ROLE_MAPPINGS_BODY = i18n.translate(
13-
'xpack.enterpriseSearch.appSearch.roleMapping.emptyRoleMappingsBody',
14-
{
15-
defaultMessage:
16-
'All users who successfully authenticate will be assigned the Owner role and have access to all engines. Add a new role to override the default.',
17-
}
18-
);
19-
2012
export const DELETE_ROLE_MAPPING_MESSAGE = i18n.translate(
2113
'xpack.enterpriseSearch.appSearch.deleteRoleMappingMessage',
2214
{

x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings.test.tsx

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@ import React from 'react';
1212

1313
import { shallow } from 'enzyme';
1414

15-
import { EuiButton, EuiEmptyPrompt } from '@elastic/eui';
16-
1715
import { Loading } from '../../../shared/loading';
18-
import { RoleMappingsTable } from '../../../shared/role_mapping';
16+
import { RoleMappingsTable, RoleMappingsHeading } from '../../../shared/role_mapping';
1917
import { wsRoleMapping } from '../../../shared/role_mapping/__mocks__/roles';
2018

2119
import { RoleMapping } from './role_mapping';
@@ -53,24 +51,16 @@ describe('RoleMappings', () => {
5351
expect(wrapper.find(Loading)).toHaveLength(1);
5452
});
5553

56-
it('renders empty state', () => {
57-
setMockValues({ ...mockValues, roleMappings: [] });
58-
const wrapper = shallow(<RoleMappings />);
59-
60-
expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1);
61-
});
62-
6354
it('renders RoleMapping flyout', () => {
6455
setMockValues({ ...mockValues, roleMappingFlyoutOpen: true });
6556
const wrapper = shallow(<RoleMappings />);
6657

6758
expect(wrapper.find(RoleMapping)).toHaveLength(1);
6859
});
6960

70-
it('handles button click', () => {
71-
setMockValues({ ...mockValues, roleMappings: [] });
61+
it('handles onClick', () => {
7262
const wrapper = shallow(<RoleMappings />);
73-
wrapper.find(EuiEmptyPrompt).dive().find(EuiButton).simulate('click');
63+
wrapper.find(RoleMappingsHeading).prop('onClick')();
7464

7565
expect(initializeRoleMapping).toHaveBeenCalled();
7666
});

x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings.tsx

Lines changed: 16 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,13 @@ import React, { useEffect } from 'react';
99

1010
import { useActions, useValues } from 'kea';
1111

12-
import {
13-
EuiButton,
14-
EuiEmptyPrompt,
15-
EuiPageContent,
16-
EuiPageContentBody,
17-
EuiPageHeader,
18-
EuiPanel,
19-
} from '@elastic/eui';
20-
2112
import { FlashMessages } from '../../../shared/flash_messages';
2213
import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
2314
import { Loading } from '../../../shared/loading';
24-
import { RoleMappingsTable } from '../../../shared/role_mapping';
25-
import {
26-
EMPTY_ROLE_MAPPINGS_TITLE,
27-
ROLE_MAPPING_ADD_BUTTON,
28-
ROLE_MAPPINGS_TITLE,
29-
ROLE_MAPPINGS_DESCRIPTION,
30-
} from '../../../shared/role_mapping/constants';
15+
import { RoleMappingsTable, RoleMappingsHeading } from '../../../shared/role_mapping';
16+
import { ROLE_MAPPINGS_TITLE } from '../../../shared/role_mapping/constants';
3117

32-
import { ROLE_MAPPINGS_ENGINE_ACCESS_HEADING, EMPTY_ROLE_MAPPINGS_BODY } from './constants';
18+
import { ROLE_MAPPINGS_ENGINE_ACCESS_HEADING } from './constants';
3319
import { RoleMapping } from './role_mapping';
3420
import { RoleMappingsLogic } from './role_mappings_logic';
3521

@@ -54,47 +40,26 @@ export const RoleMappings: React.FC = () => {
5440

5541
if (dataLoading) return <Loading />;
5642

57-
const addMappingButton = (
58-
<EuiButton fill onClick={() => initializeRoleMapping()}>
59-
{ROLE_MAPPING_ADD_BUTTON}
60-
</EuiButton>
61-
);
62-
63-
const roleMappingEmptyState = (
64-
<EuiPanel paddingSize="l" color="subdued" hasBorder={false}>
65-
<EuiEmptyPrompt
66-
iconType="usersRolesApp"
67-
title={<h2>{EMPTY_ROLE_MAPPINGS_TITLE}</h2>}
68-
body={<p>{EMPTY_ROLE_MAPPINGS_BODY}</p>}
69-
actions={addMappingButton}
43+
const roleMappingsSection = (
44+
<>
45+
<RoleMappingsHeading productName="App Search" onClick={() => initializeRoleMapping()} />
46+
<RoleMappingsTable
47+
roleMappings={roleMappings}
48+
accessItemKey="engines"
49+
accessHeader={ROLE_MAPPINGS_ENGINE_ACCESS_HEADING}
50+
initializeRoleMapping={initializeRoleMapping}
51+
shouldShowAuthProvider={multipleAuthProvidersConfig}
52+
handleDeleteMapping={handleDeleteMapping}
7053
/>
71-
</EuiPanel>
72-
);
73-
74-
const roleMappingsTable = (
75-
<RoleMappingsTable
76-
roleMappings={roleMappings}
77-
accessItemKey="engines"
78-
accessHeader={ROLE_MAPPINGS_ENGINE_ACCESS_HEADING}
79-
addMappingButton={addMappingButton}
80-
initializeRoleMapping={initializeRoleMapping}
81-
shouldShowAuthProvider={multipleAuthProvidersConfig}
82-
handleDeleteMapping={handleDeleteMapping}
83-
/>
54+
</>
8455
);
8556

8657
return (
8758
<>
8859
<SetPageChrome trail={[ROLE_MAPPINGS_TITLE]} />
89-
<EuiPageHeader pageTitle={ROLE_MAPPINGS_TITLE} description={ROLE_MAPPINGS_DESCRIPTION} />
90-
9160
{roleMappingFlyoutOpen && <RoleMapping />}
92-
<EuiPageContent hasShadow={false} hasBorder={roleMappings.length > 0}>
93-
<EuiPageContentBody>
94-
<FlashMessages />
95-
{roleMappings.length === 0 ? roleMappingEmptyState : roleMappingsTable}
96-
</EuiPageContentBody>
97-
</EuiPageContent>
61+
<FlashMessages />
62+
{roleMappingsSection}
9863
</>
9964
);
10065
};

x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import { i18n } from '@kbn/i18n';
99

10+
import { ProductName } from '../types';
11+
1012
export const ANY_AUTH_PROVIDER = '*';
1113

1214
export const ANY_AUTH_PROVIDER_OPTION_LABEL = i18n.translate(
@@ -104,7 +106,7 @@ export const DELETE_ROLE_MAPPING_BUTTON = i18n.translate(
104106
export const FILTER_ROLE_MAPPINGS_PLACEHOLDER = i18n.translate(
105107
'xpack.enterpriseSearch.roleMapping.filterRoleMappingsPlaceholder',
106108
{
107-
defaultMessage: 'Filter roles...',
109+
defaultMessage: 'Filter role mappings',
108110
}
109111
);
110112

@@ -125,21 +127,6 @@ export const MANAGE_ROLE_MAPPING_TITLE = i18n.translate(
125127
{ defaultMessage: 'Manage role mapping' }
126128
);
127129

128-
export const EMPTY_ROLE_MAPPINGS_TITLE = i18n.translate(
129-
'xpack.enterpriseSearch.roleMapping.emptyRoleMappingsTitle',
130-
{
131-
defaultMessage: 'No role mappings yet',
132-
}
133-
);
134-
135-
export const ROLE_MAPPINGS_DESCRIPTION = i18n.translate(
136-
'xpack.enterpriseSearch.roleMapping.roleMappingsDescription',
137-
{
138-
defaultMessage:
139-
'Define role mappings for elasticsearch-native and elasticsearch-saml authentication.',
140-
}
141-
);
142-
143130
export const ROLE_MAPPING_NOT_FOUND = i18n.translate(
144131
'xpack.enterpriseSearch.roleMapping.notFoundMessage',
145132
{
@@ -168,13 +155,6 @@ export const ROLE_MAPPING_FLYOUT_DESCRIPTION = i18n.translate(
168155
}
169156
);
170157

171-
export const ROLE_MAPPING_ADD_BUTTON = i18n.translate(
172-
'xpack.enterpriseSearch.roleMapping.roleMappingAddButton',
173-
{
174-
defaultMessage: 'Add mapping',
175-
}
176-
);
177-
178158
export const ROLE_MAPPING_FLYOUT_CREATE_BUTTON = i18n.translate(
179159
'xpack.enterpriseSearch.roleMapping.roleMappingFlyoutCreateButton',
180160
{
@@ -198,3 +178,25 @@ export const UPDATE_ROLE_MAPPING = i18n.translate(
198178
'xpack.enterpriseSearch.roleMapping.updateRoleMappingButtonLabel',
199179
{ defaultMessage: 'Update role mapping' }
200180
);
181+
182+
export const ROLE_MAPPINGS_HEADING_TITLE = i18n.translate(
183+
'xpack.enterpriseSearch.roleMapping.roleMappingsHeadingTitle',
184+
{ defaultMessage: 'Role mappings' }
185+
);
186+
187+
export const ROLE_MAPPINGS_HEADING_DESCRIPTION = (productName: ProductName) =>
188+
i18n.translate('xpack.enterpriseSearch.roleMapping.roleMappingsHeadingDescription', {
189+
defaultMessage:
190+
'Role mappings provide an interface to associate native or SAML-governed role attributes with {productName} permissions.',
191+
values: { productName },
192+
});
193+
194+
export const ROLE_MAPPINGS_HEADING_DOCS_LINK = i18n.translate(
195+
'xpack.enterpriseSearch.roleMapping.roleMappingsHeadingDocsLink',
196+
{ defaultMessage: 'Learn more about role mappings' }
197+
);
198+
199+
export const ROLE_MAPPINGS_HEADING_BUTTON = i18n.translate(
200+
'xpack.enterpriseSearch.roleMapping.roleMappingsHeadingButton',
201+
{ defaultMessage: 'Create a new role mapping' }
202+
);

x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ export { RoleMappingsTable } from './role_mappings_table';
1010
export { RoleOptionLabel } from './role_option_label';
1111
export { RoleSelector } from './role_selector';
1212
export { RoleMappingFlyout } from './role_mapping_flyout';
13+
export { RoleMappingsHeading } from './role_mappings_heading';
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import React from 'react';
9+
10+
import { shallow } from 'enzyme';
11+
12+
import { EuiTitle, EuiLink, EuiButton, EuiText } from '@elastic/eui';
13+
14+
import { RoleMappingsHeading } from './role_mappings_heading';
15+
16+
describe('RoleMappingsHeading', () => {
17+
it('renders ', () => {
18+
const wrapper = shallow(<RoleMappingsHeading productName="App Search" onClick={jest.fn()} />);
19+
20+
expect(wrapper.find(EuiTitle)).toHaveLength(1);
21+
expect(wrapper.find(EuiText)).toHaveLength(1);
22+
expect(wrapper.find(EuiLink)).toHaveLength(1);
23+
expect(wrapper.find(EuiButton)).toHaveLength(1);
24+
});
25+
});
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import React from 'react';
9+
10+
import {
11+
EuiButton,
12+
EuiFlexGroup,
13+
EuiFlexItem,
14+
EuiLink,
15+
EuiSpacer,
16+
EuiText,
17+
EuiTitle,
18+
} from '@elastic/eui';
19+
20+
import { ProductName } from '../types';
21+
22+
import {
23+
ROLE_MAPPINGS_HEADING_TITLE,
24+
ROLE_MAPPINGS_HEADING_DESCRIPTION,
25+
ROLE_MAPPINGS_HEADING_DOCS_LINK,
26+
ROLE_MAPPINGS_HEADING_BUTTON,
27+
} from './constants';
28+
29+
interface Props {
30+
productName: ProductName;
31+
onClick(): void;
32+
}
33+
34+
// TODO: Replace EuiLink href with acutal docs link when available
35+
const ROLE_MAPPINGS_DOCS_HREF = '#TODO';
36+
37+
export const RoleMappingsHeading: React.FC<Props> = ({ productName, onClick }) => (
38+
<>
39+
<EuiFlexGroup justifyContent="spaceBetween">
40+
<EuiFlexItem>
41+
<EuiTitle>
42+
<h2>{ROLE_MAPPINGS_HEADING_TITLE}</h2>
43+
</EuiTitle>
44+
<EuiSpacer size="xs" />
45+
<EuiText color="subdued">
46+
<p>
47+
{ROLE_MAPPINGS_HEADING_DESCRIPTION(productName)}{' '}
48+
<EuiLink external href={ROLE_MAPPINGS_DOCS_HREF} target="_blank">
49+
{ROLE_MAPPINGS_HEADING_DOCS_LINK}
50+
</EuiLink>
51+
</p>
52+
</EuiText>
53+
</EuiFlexItem>
54+
<EuiFlexItem grow={false}>
55+
<EuiButton fill onClick={onClick}>
56+
{ROLE_MAPPINGS_HEADING_BUTTON}
57+
</EuiButton>
58+
</EuiFlexItem>
59+
</EuiFlexGroup>
60+
<EuiSpacer />
61+
</>
62+
);

x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import React, { Fragment, useState } from 'react';
1010
import {
1111
EuiButtonIcon,
1212
EuiFieldSearch,
13-
EuiFlexGroup,
14-
EuiFlexItem,
1513
EuiIconTip,
1614
EuiSpacer,
1715
EuiTable,
@@ -54,7 +52,6 @@ interface Props {
5452
accessItemKey: 'groups' | 'engines';
5553
accessHeader: string;
5654
roleMappings: Array<ASRoleMapping | WSRoleMapping>;
57-
addMappingButton: React.ReactNode;
5855
accessAllEngines?: boolean;
5956
shouldShowAuthProvider?: boolean;
6057
initializeRoleMapping(roleMappingId: string): void;
@@ -72,7 +69,6 @@ export const RoleMappingsTable: React.FC<Props> = ({
7269
accessItemKey,
7370
accessHeader,
7471
roleMappings,
75-
addMappingButton,
7672
shouldShowAuthProvider,
7773
initializeRoleMapping,
7874
handleDeleteMapping,
@@ -117,16 +113,11 @@ export const RoleMappingsTable: React.FC<Props> = ({
117113

118114
return (
119115
<>
120-
<EuiFlexGroup justifyContent="spaceBetween">
121-
<EuiFlexItem>
122-
<EuiFieldSearch
123-
value={filterValue}
124-
placeholder={FILTER_ROLE_MAPPINGS_PLACEHOLDER}
125-
onChange={(e) => updateValue(e.target.value)}
126-
/>
127-
</EuiFlexItem>
128-
<EuiFlexItem grow={false}>{addMappingButton}</EuiFlexItem>
129-
</EuiFlexGroup>
116+
<EuiFieldSearch
117+
value={filterValue}
118+
placeholder={FILTER_ROLE_MAPPINGS_PLACEHOLDER}
119+
onChange={(e) => updateValue(e.target.value)}
120+
/>
130121
<EuiSpacer />
131122
{filteredResults.length > 0 ? (
132123
<EuiTable className="roleMappingsTable">

x-pack/plugins/enterprise_search/public/applications/shared/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,5 @@ export interface RoleMapping {
3535
content: string;
3636
};
3737
}
38+
39+
export type ProductName = 'App Search' | 'Workplace Search';

x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/constants.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,6 @@ export const GROUP_ASSIGNMENT_LABEL = i18n.translate(
7373
}
7474
);
7575

76-
export const EMPTY_ROLE_MAPPINGS_BODY = i18n.translate(
77-
'xpack.enterpriseSearch.workplaceSearch.roleMapping.emptyRoleMappingsBody',
78-
{
79-
defaultMessage:
80-
'New team members are assigned the admin role by default. An admin can access everything. Create a new role to override the default.',
81-
}
82-
);
83-
8476
export const ROLE_MAPPINGS_TABLE_HEADER = i18n.translate(
8577
'xpack.enterpriseSearch.workplaceSearch.roleMapping.roleMappingsTableHeader',
8678
{

0 commit comments

Comments
 (0)