Skip to content

Commit ee6b643

Browse files
authored
Merge pull request #12053 from aaleksee-akamai/UIE-8671-facade-roles
feat: [UIE-8671] - IAM RBAC: facade roles description
2 parents e28791d + 8aa53e2 commit ee6b643

12 files changed

Lines changed: 240 additions & 102 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/api-v4": Upcoming Features
3+
---
4+
5+
added new types for iam ([#12053](https://github.com/linode/manager/pull/12053))

packages/api-v4/src/iam/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ export type AccountAccessRole =
1818
| 'account_volume_admin'
1919
| 'firewall_creator'
2020
| 'linode_contributor'
21-
| 'linode_creator';
21+
| 'linode_creator'
22+
| 'stackscript_creator';
2223

2324
export type EntityAccessRole =
2425
| 'database_admin'
@@ -27,6 +28,8 @@ export type EntityAccessRole =
2728
| 'linode_contributor'
2829
| 'linode_creator'
2930
| 'linode_viewer'
31+
| 'stackscript_admin'
32+
| 'stackscript_viewer'
3033
| 'update_firewall';
3134

3235
export interface IamUserPermissions {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Upcoming Features
3+
---
4+
5+
implement a logic for getting a description for the facade roles ([#12053](https://github.com/linode/manager/pull/12053))

packages/manager/src/features/IAM/Roles/RolesTable/RolesTable.tsx

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
1-
import { Button, Select } from '@linode/ui';
1+
import { Button, Select, Typography } from '@linode/ui';
22
import { capitalizeAllWords } from '@linode/utilities';
33
import Grid from '@mui/material/Grid2';
44
import Paper from '@mui/material/Paper';
55
import {
6+
sortRows,
67
Table,
78
TableBody,
89
TableCell,
910
TableHead,
1011
TableHeaderCell,
1112
TableRow,
1213
TableRowExpanded,
13-
sortRows,
1414
} from 'akamai-cds-react-components/Table';
1515
import React, { useState } from 'react';
1616

1717
import { DebouncedSearchTextField } from 'src/components/DebouncedSearchTextField';
18-
import { StyledTextTooltip } from 'src/features/components/PlansPanel/PlansAvailabilityNotice.styles';
18+
import { Link } from 'src/components/Link';
1919
import { RolesTableActionMenu } from 'src/features/IAM/Roles/RolesTable/RolesTableActionMenu';
2020
import { RolesTableExpandedRow } from 'src/features/IAM/Roles/RolesTable/RolesTableExpandedRow';
21-
import { mapEntityTypesForSelect } from 'src/features/IAM/Shared/utilities';
21+
import {
22+
getFacadeRoleDescription,
23+
mapEntityTypesForSelect,
24+
} from 'src/features/IAM/Shared/utilities';
2225

2326
import type { SelectOption } from '@linode/ui';
2427
import type { Order } from 'akamai-cds-react-components/Table';
@@ -45,16 +48,20 @@ export const RolesTable = ({ roles }: Props) => {
4548
}, [roles]);
4649

4750
const [filterableEntityType, setFilterableEntityType] =
48-
useState<SelectOption | null>(ALL_ROLES_OPTION);
51+
useState<null | SelectOption>(ALL_ROLES_OPTION);
4952

5053
const [sort, setSort] = useState<
51-
{ column: string; order: Order } | undefined
54+
undefined | { column: string; order: Order }
5255
>(undefined);
5356

5457
const [selectedRows, setSelectedRows] = useState<RoleMap[]>([]);
5558

5659
const areAllSelected = React.useMemo(() => {
57-
return !!rows?.length && !!selectedRows?.length && rows?.length === selectedRows?.length;
60+
return (
61+
!!rows?.length &&
62+
!!selectedRows?.length &&
63+
rows?.length === selectedRows?.length
64+
);
5865
}, [rows, selectedRows]);
5966

6067
const handleSort = (event: CustomEvent, column: string) => {
@@ -107,20 +114,18 @@ export const RolesTable = ({ roles }: Props) => {
107114
return (
108115
<Paper sx={(theme) => ({ marginTop: theme.spacing(2) })}>
109116
<Grid
110-
sx={() => ({
111-
justifyContent: 'space-between',
112-
})}
113117
container
114118
direction="row"
115119
spacing={2}
120+
sx={{ justifyContent: 'space-between' }}
116121
>
117122
<Grid
118-
sx={() => ({
119-
alignItems: 'center',
120-
justifyContent: 'flex-start',
121-
})}
122123
container
123124
direction="row"
125+
sx={{
126+
alignItems: 'center',
127+
justifyContent: 'flex-start',
128+
}}
124129
>
125130
<DebouncedSearchTextField
126131
clearable
@@ -143,14 +148,14 @@ export const RolesTable = ({ roles }: Props) => {
143148
/>
144149
</Grid>
145150
<Button
151+
buttonType="primary"
152+
disabled={selectedRows.length === 0}
153+
onClick={() => handleAssignSelectedRoles()}
146154
tooltipText={
147155
selectedRows.length === 0
148156
? 'You must select some roles to assign them.'
149157
: undefined
150158
}
151-
buttonType="primary"
152-
disabled={selectedRows.length === 0}
153-
onClick={() => handleAssignSelectedRoles()}
154159
>
155160
Assign Selected Roles
156161
</Button>
@@ -176,19 +181,19 @@ export const RolesTable = ({ roles }: Props) => {
176181
sort={(event) => handleSort(event, 'access')}
177182
sortable
178183
sorted={sort?.column === 'access' ? sort.order : undefined}
179-
style={{ minWidth: '18%' }}
184+
style={{ minWidth: '14%' }}
180185
>
181186
Role Type
182187
</TableHeaderCell>
183188
<TableHeaderCell
184189
sort={(event) => handleSort(event, 'description')}
185190
sortable
186191
sorted={sort?.column === 'description' ? sort.order : undefined}
187-
style={{ minWidth: '30%' }}
192+
style={{ minWidth: '38%' }}
188193
>
189194
Description
190195
</TableHeaderCell>
191-
<TableHeaderCell style={{ minWidth: '14%' }} />
196+
<TableHeaderCell style={{ minWidth: '10%' }} />
192197
</TableRow>
193198
</TableHead>
194199
<TableBody>
@@ -210,20 +215,21 @@ export const RolesTable = ({ roles }: Props) => {
210215
<TableCell style={{ minWidth: '26%' }}>
211216
{roleRow.name}
212217
</TableCell>
213-
<TableCell style={{ minWidth: '18%' }}>
218+
<TableCell style={{ minWidth: '14%' }}>
214219
{capitalizeAllWords(roleRow.access, '_')}
215220
</TableCell>
216-
<TableCell style={{ minWidth: '30%' }}>
217-
{roleRow.description.length <= 80 ? (
218-
<>{roleRow.description}</>
221+
<TableCell style={{ minWidth: '38%' }}>
222+
{roleRow.permissions.length ? (
223+
roleRow.description
219224
) : (
220-
<span>
221-
{roleRow.description.substring(0, 80)}{'... '}
222-
<StyledTextTooltip tooltipText={roleRow.description} displayText={'Show more'}/>
223-
</span>
225+
// TODO: update the link for the description when it's ready - UIE-8534
226+
<Typography>
227+
{getFacadeRoleDescription(roleRow)}{' '}
228+
<Link to="#">Learn more.</Link>
229+
</Typography>
224230
)}
225231
</TableCell>
226-
<TableCell style={{ minWidth: '14%' }}>
232+
<TableCell style={{ minWidth: '10%' }}>
227233
<RolesTableActionMenu />
228234
</TableCell>
229235
<TableRowExpanded

packages/manager/src/features/IAM/Shared/AssignedPermissionsPanel/AssignedPermissionsPanel.tsx

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
import { Paper, StyledLinkButton, Typography } from '@linode/ui';
2-
import { truncate } from '@linode/utilities';
1+
import { Paper, Typography } from '@linode/ui';
32
import { useTheme } from '@mui/material';
43
import * as React from 'react';
54

5+
import { Link } from 'src/components/Link';
6+
67
import { Entities } from '../Entities/Entities';
78
import { Permissions } from '../Permissions/Permissions';
8-
9-
import type {
10-
DrawerModes,
11-
EntitiesOption,
12-
ExtendedRole,
13-
ExtendedRoleMap,
9+
import {
10+
type DrawerModes,
11+
type EntitiesOption,
12+
type ExtendedRole,
13+
type ExtendedRoleMap,
14+
getFacadeRoleDescription,
1415
} from '../utilities';
16+
1517
import type { SxProps, Theme } from '@mui/material';
1618

1719
interface Props {
@@ -31,15 +33,9 @@ export const AssignedPermissionsPanel = ({
3133
sx,
3234
value,
3335
}: Props) => {
34-
const [showFullDescription, setShowFullDescription] = React.useState(false);
35-
3636
const theme = useTheme();
3737

38-
const description =
39-
role.description.length < 110 || showFullDescription
40-
? role.description
41-
: truncate(role.description, 110);
42-
38+
// TODO: update the link for the description when it's ready - UIE-8534
4339
return (
4440
<Paper
4541
sx={{
@@ -61,25 +57,18 @@ export const AssignedPermissionsPanel = ({
6157
</Typography>
6258
<Typography
6359
sx={{
64-
display: 'flex',
65-
flexDirection: 'column',
6660
marginBottom: theme.tokens.spacing.S8,
61+
marginTop: theme.tokens.spacing.S2,
6762
overflowWrap: 'anywhere',
6863
wordBreak: 'normal',
6964
}}
7065
>
71-
{description}{' '}
72-
{description.length > 110 && (
73-
<StyledLinkButton
74-
onClick={() => setShowFullDescription((show) => !show)}
75-
sx={{
76-
font: theme.tokens.alias.Typography.Label.Semibold.Xs,
77-
width: 'max-content',
78-
}}
79-
type="button"
80-
>
81-
{showFullDescription ? 'Hide' : 'Expand'}
82-
</StyledLinkButton>
66+
{role.permissions.length ? (
67+
role.description
68+
) : (
69+
<>
70+
{getFacadeRoleDescription(role)} <Link to="#">Learn more.</Link>
71+
</>
8372
)}
8473
</Typography>
8574
<Permissions permissions={role.permissions} />

packages/manager/src/features/IAM/Shared/AssignedRolesTable/AssignedRolesTable.tsx

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
1-
import {
2-
Autocomplete,
3-
CircleProgress,
4-
StyledLinkButton,
5-
Typography,
6-
} from '@linode/ui';
7-
import { capitalize, truncate } from '@linode/utilities';
8-
import { Grid, useTheme } from '@mui/material';
1+
import { Autocomplete, CircleProgress, Typography } from '@linode/ui';
2+
import { useTheme } from '@mui/material';
3+
import Grid from '@mui/material/Grid2';
94
import React from 'react';
105
import { useHistory, useParams } from 'react-router-dom';
116

127
import { CollapsibleTable } from 'src/components/CollapsibleTable/CollapsibleTable';
138
import { DebouncedSearchTextField } from 'src/components/DebouncedSearchTextField';
9+
import { Link } from 'src/components/Link';
1410
import { TableCell } from 'src/components/TableCell';
1511
import { TableRow } from 'src/components/TableRow';
1612
import { TableRowEmpty } from 'src/components/TableRowEmpty/TableRowEmpty';
@@ -28,7 +24,9 @@ import { RemoveAssignmentConfirmationDialog } from '../RemoveAssignmentConfirmat
2824
import {
2925
addEntitiesNamesToRoles,
3026
combineRoles,
27+
getFacadeRoleDescription,
3128
getFilteredRoles,
29+
getFormattedEntityType,
3230
mapEntityTypes,
3331
mapRolesToPermissions,
3432
transformedAccountEntities,
@@ -126,8 +124,6 @@ export const AssignedRolesTable = () => {
126124

127125
const [entityType, setEntityType] = React.useState<EntitiesType | null>(null);
128126

129-
const [showFullDescription, setShowFullDescription] = React.useState(false);
130-
131127
const handleViewEntities = (
132128
roleName: AccountAccessRole | EntityAccessRole
133129
) => {
@@ -154,7 +150,7 @@ export const AssignedRolesTable = () => {
154150
<Typography>
155151
{role.entity_type === 'account'
156152
? 'All Entities'
157-
: `All ${capitalize(role.entity_type)}s`}
153+
: `All ${getFormattedEntityType(role.entity_type)}s`}
158154
</Typography>
159155
</TableCell>
160156
) : (
@@ -177,12 +173,7 @@ export const AssignedRolesTable = () => {
177173
</TableCell>
178174
</>
179175
);
180-
181-
const description =
182-
role.description.length < 150 || showFullDescription
183-
? role.description
184-
: truncate(role.description, 150);
185-
176+
// TODO: update the link for 'Learn more' in the description when it's ready - UIE-8534
186177
const InnerTable = (
187178
<Grid
188179
sx={{
@@ -197,20 +188,16 @@ export const AssignedRolesTable = () => {
197188
Description
198189
</Typography>
199190
<Typography
200-
sx={{ display: 'flex', flexDirection: 'column', marginBottom: 1 }}
191+
sx={{
192+
marginBottom: theme.tokens.spacing.S8,
193+
}}
201194
>
202-
{' '}
203-
{description}{' '}
204-
{description.length > 150 && (
205-
<StyledLinkButton
206-
onClick={() => setShowFullDescription((show) => !show)}
207-
sx={{
208-
font: theme.tokens.alias.Typography.Label.Semibold.Xs,
209-
width: 'max-content',
210-
}}
211-
>
212-
{showFullDescription ? 'Hide' : 'Expand'}
213-
</StyledLinkButton>
195+
{role.permissions.length ? (
196+
role.description
197+
) : (
198+
<>
199+
{getFacadeRoleDescription(role)} <Link to="#">Learn more.</Link>
200+
</>
214201
)}
215202
</Typography>
216203
<Permissions permissions={role.permissions} />
@@ -224,7 +211,7 @@ export const AssignedRolesTable = () => {
224211
label: role.name,
225212
};
226213
});
227-
}, [roles, query, entityType, showFullDescription]);
214+
}, [roles, query, entityType]);
228215

229216
if (accountPermissionsLoading || entitiesLoading || assignedRolesLoading) {
230217
return <CircleProgress />;

packages/manager/src/features/IAM/Shared/Entities/Entities.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ describe('Entities', () => {
7373
const autocomplete = screen.queryAllByRole('combobox');
7474

7575
expect(screen.getByText('Entities')).toBeVisible();
76-
expect(screen.getByText('All firewalls')).toBeVisible();
76+
expect(screen.getByText('All Firewalls')).toBeVisible();
7777

7878
// check that the autocomplete doesn't exist
7979
expect(autocomplete.length).toBe(0);

0 commit comments

Comments
 (0)