Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upcoming: [M3-7613] - Grants and Permissions for Placement Groups #10257

Merged
merged 9 commits into from
Mar 22, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Set up grants and permissions for Placement Groups ([#10257](https://github.com/linode/manager/pull/10257))
6 changes: 6 additions & 0 deletions packages/manager/src/components/Breadcrumb/Breadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export interface BreadcrumbProps {
* An array of objects that can be used to customize any crumb.
*/
crumbOverrides?: CrumbOverridesProps[];
/**
* A boolean that if true will disable the pencil icon button.
*/
disabledBreadcrumbEditButton?: boolean;
/**
* A boolean that if true will only show the first and last crumb.
*/
Expand Down Expand Up @@ -48,6 +52,7 @@ export const Breadcrumb = (props: BreadcrumbProps) => {
const {
breadcrumbDataAttrs,
crumbOverrides,
disabledBreadcrumbEditButton,
firstAndLastOnly,
labelOptions,
labelTitle,
Expand Down Expand Up @@ -75,6 +80,7 @@ export const Breadcrumb = (props: BreadcrumbProps) => {
>
<Crumbs
crumbOverrides={crumbOverrides}
disabledBreadcrumbEditButton={disabledBreadcrumbEditButton}
firstAndLastOnly={firstAndLastOnly}
labelOptions={labelOptions}
labelTitle={labelTitle}
Expand Down
3 changes: 3 additions & 0 deletions packages/manager/src/components/Breadcrumb/Crumbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface CrumbOverridesProps {

interface Props {
crumbOverrides?: CrumbOverridesProps[];
disabledBreadcrumbEditButton?: boolean;
firstAndLastOnly?: boolean;
labelOptions?: LabelProps;
labelTitle?: string;
Expand All @@ -30,6 +31,7 @@ interface Props {
export const Crumbs = React.memo((props: Props) => {
const {
crumbOverrides,
disabledBreadcrumbEditButton,
firstAndLastOnly,
labelOptions,
labelTitle,
Expand Down Expand Up @@ -92,6 +94,7 @@ export const Crumbs = React.memo((props: Props) => {
{/* the final crumb has the possibility of being a link, editable text or just static text */}
<FinalCrumb
crumb={labelTitle || lastCrumb}
disabledBreadcrumbEditButton={disabledBreadcrumbEditButton}
labelOptions={labelOptions}
onEditHandlers={onEditHandlers}
/>
Expand Down
9 changes: 8 additions & 1 deletion packages/manager/src/components/Breadcrumb/FinalCrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,24 @@ import { EditableProps, LabelProps } from './types';

interface Props {
crumb: string;
disabledBreadcrumbEditButton?: boolean;
labelOptions?: LabelProps;
onEditHandlers?: EditableProps;
}

export const FinalCrumb = React.memo((props: Props) => {
const { crumb, labelOptions, onEditHandlers } = props;
const {
crumb,
disabledBreadcrumbEditButton,
labelOptions,
onEditHandlers,
} = props;

if (onEditHandlers) {
return (
<StyledEditableText
data-qa-editable-text
disabledBreadcrumbEditButton={disabledBreadcrumbEditButton}
errorText={onEditHandlers.errorText}
handleAnalyticsEvent={onEditHandlers.handleAnalyticsEvent}
labelLink={labelOptions && labelOptions.linkTo}
Expand Down
2 changes: 1 addition & 1 deletion packages/manager/src/components/Drawer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Close from '@mui/icons-material/Close';
import _Drawer, { DrawerProps } from '@mui/material/Drawer';
import Grid from '@mui/material/Unstable_Grid2';
import { Theme } from '@mui/material/styles';
import Grid from '@mui/material/Unstable_Grid2';
import * as React from 'react';
import { makeStyles } from 'tss-react/mui';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ const useStyles = makeStyles<void, 'editIcon' | 'icon'>()(

interface Props {
className?: string;
disabledBreadcrumbEditButton?: boolean;
errorText?: string;
/**
* Send event analytics
Expand Down Expand Up @@ -139,6 +140,7 @@ export const EditableText = (props: PassThroughProps) => {
const [text, setText] = React.useState(props.text);
const {
className,
disabledBreadcrumbEditButton,
errorText,
handleAnalyticsEvent,
labelLink,
Expand Down Expand Up @@ -228,6 +230,7 @@ export const EditableText = (props: PassThroughProps) => {
aria-label={`Edit ${text}`}
className={`${classes.button} ${classes.editIcon}`}
data-qa-edit-button
disabled={disabledBreadcrumbEditButton}
onClick={openEdit}
>
<Edit className={classes.icon} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Grid from '@mui/material/Unstable_Grid2';
import { Theme, styled, useTheme } from '@mui/material/styles';
import Grid from '@mui/material/Unstable_Grid2';
import useMediaQuery from '@mui/material/useMediaQuery';
import * as React from 'react';

Expand All @@ -18,6 +18,7 @@ export interface LandingHeaderProps {
breadcrumbProps?: BreadcrumbProps;
buttonDataAttrs?: { [key: string]: boolean | string };
createButtonText?: string;
disabledBreadcrumbEditButton?: boolean;
disabledCreateButton?: boolean;
docsLabel?: string;
docsLink?: string;
Expand All @@ -43,6 +44,7 @@ export const LandingHeader = ({
breadcrumbProps,
buttonDataAttrs,
createButtonText,
disabledBreadcrumbEditButton,
disabledCreateButton,
docsLabel,
docsLink,
Expand Down Expand Up @@ -89,6 +91,7 @@ export const LandingHeader = ({
removeCrumbX={removeCrumbX}
{...breadcrumbDataAttrs}
{...breadcrumbProps}
disabledBreadcrumbEditButton={disabledBreadcrumbEditButton}
/>
</Grid>
{!shouldHideDocsAndCreateButtons && (
Expand Down
1 change: 1 addition & 0 deletions packages/manager/src/features/Account/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const grantTypeMap = {
linode: 'Linodes',
longview: 'Longview Clients',
nodebalancer: 'NodeBalancers',
placementGroups: 'Placement Groups',
stackscript: 'StackScripts',
volume: 'Volumes',
vpc: 'VPCs',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@ import { Typography } from 'src/components/Typography';
import type { FormikHelpers } from 'formik';

interface Props {
disabledPlacementGroupCreateButton: boolean;
handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
setFieldValue: FormikHelpers<any>['setFieldValue'];
value: boolean;
}

export const PlacementGroupsAffinityEnforcementRadioGroup = (props: Props) => {
const { handleChange, setFieldValue, value } = props;
const {
disabledPlacementGroupCreateButton,
handleChange,
setFieldValue,
value,
} = props;
return (
<Box sx={{ pt: 2 }}>
<Notice
Expand Down Expand Up @@ -45,6 +51,7 @@ export const PlacementGroupsAffinityEnforcementRadioGroup = (props: Props) => {
</Typography>
}
control={<Radio />}
disabled={disabledPlacementGroupCreateButton}
value={true}
/>
<FormControlLabel
Expand All @@ -56,6 +63,7 @@ export const PlacementGroupsAffinityEnforcementRadioGroup = (props: Props) => {
</Typography>
}
control={<Radio />}
disabled={disabledPlacementGroupCreateButton}
sx={{ mt: 2 }}
value={false}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import { affinityTypeOptions } from './utils';
import type { FormikHelpers } from 'formik';

interface Props {
disabledPlacementGroupCreateButton: boolean;
error: string | undefined;
setFieldValue: FormikHelpers<any>['setFieldValue'];
}

export const PlacementGroupsAffinityTypeSelect = (props: Props) => {
const { error, setFieldValue } = props;
const { disabledPlacementGroupCreateButton, error, setFieldValue } = props;
return (
<Autocomplete
defaultValue={affinityTypeOptions.find(
Expand Down Expand Up @@ -83,6 +84,7 @@ export const PlacementGroupsAffinityTypeSelect = (props: Props) => {
),
}}
disableClearable={true}
disabled={disabledPlacementGroupCreateButton}
errorText={error}
label="Affinity Type"
options={affinityTypeOptions}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { PlacementGroupsCreateDrawer } from './PlacementGroupsCreateDrawer';

const commonProps = {
allPlacementGroups: [],
disabledPlacementGroupCreateButton: false,
onClose: vi.fn(),
open: true,
};
Expand Down
mjac0bs marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { RegionSelect } from 'src/components/RegionSelect/RegionSelect';
import { Stack } from 'src/components/Stack';
import { TextField } from 'src/components/TextField';
import { Typography } from 'src/components/Typography';
import { getRestrictedResourceText } from 'src/features/Account/utils';
import { useFormValidateOnChange } from 'src/hooks/useFormValidateOnChange';
import { useCreatePlacementGroup } from 'src/queries/placementGroups';
import { useRegionsQuery } from 'src/queries/regions/regions';
Expand All @@ -30,6 +31,7 @@ export const PlacementGroupsCreateDrawer = (
) => {
const {
allPlacementGroups,
disabledPlacementGroupCreateButton,
onClose,
onPlacementGroupCreate,
open,
Expand Down Expand Up @@ -131,6 +133,16 @@ export const PlacementGroupsCreateDrawer = (
open={open}
title="Create Placement Group"
>
{disabledPlacementGroupCreateButton && (
<Notice
text={getRestrictedResourceText({
action: 'edit',
resourceType: 'Placement Groups',
})}
spacingTop={16}
variant="error"
/>
)}
<form onSubmit={handleSubmit}>
<Stack spacing={1}>
{generalError && <Notice text={generalError} variant="error" />}
Expand All @@ -146,7 +158,7 @@ export const PlacementGroupsCreateDrawer = (
autoFocus: true,
}}
aria-label="Label for the Placement Group"
disabled={false}
disabled={disabledPlacementGroupCreateButton || false}
errorText={errors.label}
label="Label"
name="label"
Expand All @@ -156,6 +168,9 @@ export const PlacementGroupsCreateDrawer = (
/>
{!selectedRegionId && (
<RegionSelect
disabled={
Boolean(selectedRegionId) || disabledPlacementGroupCreateButton
}
errorText={
hasRegionReachedPGCapacity
? 'This region has reached capacity'
Expand All @@ -165,25 +180,33 @@ export const PlacementGroupsCreateDrawer = (
handleRegionSelect(selection);
}}
currentCapability="Placement Group"
disabled={Boolean(selectedRegionId)}
helperText="Only regions supporting Placement Groups are listed."
regions={regions ?? []}
selectedId={selectedRegionId ?? values.region}
/>
)}
<PlacementGroupsAffinityTypeSelect
disabledPlacementGroupCreateButton={
disabledPlacementGroupCreateButton
}
error={errors.affinity_type}
setFieldValue={setFieldValue}
/>
<PlacementGroupsAffinityEnforcementRadioGroup
disabledPlacementGroupCreateButton={
disabledPlacementGroupCreateButton
}
handleChange={handleChange}
setFieldValue={setFieldValue}
value={values.is_strict}
/>
<ActionsPanel
primaryButtonProps={{
'data-testid': 'submit',
disabled: isSubmitting || hasRegionReachedPGCapacity,
disabled:
isSubmitting ||
hasRegionReachedPGCapacity ||
disabledPlacementGroupCreateButton,
label: 'Create Placement Group',
loading: isSubmitting,
onClick: () => setHasFormBeenSubmitted(true),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ describe('PlacementGroupsDeleteModal', () => {

let renderResult: RenderResult;
await act(async () => {
renderResult = renderWithTheme(<PlacementGroupsDeleteModal {...props} />);
renderResult = renderWithTheme(
<PlacementGroupsDeleteModal disableUnassignButton={false} {...props} />
);
});

const { getByRole, getByTestId, getByText } = renderResult!;
Expand Down Expand Up @@ -138,7 +140,9 @@ describe('PlacementGroupsDeleteModal', () => {

let renderResult: RenderResult;
await act(async () => {
renderResult = renderWithTheme(<PlacementGroupsDeleteModal {...props} />);
renderResult = renderWithTheme(
<PlacementGroupsDeleteModal disableUnassignButton={false} {...props} />
);
});

const { getByRole, getByTestId, getByText } = renderResult!;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ import type {
} from '@linode/api-v4';

interface Props {
disableUnassignButton: boolean;
onClose: () => void;
open: boolean;
}

export const PlacementGroupsDeleteModal = (props: Props) => {
const { onClose, open } = props;
const { disableUnassignButton, onClose, open } = props;
const { id } = useParams<{ id: string }>();
const { data: selectedPlacementGroup } = usePlacementGroupQuery(
+id,
Expand Down Expand Up @@ -156,6 +157,7 @@ export const PlacementGroupsDeleteModal = (props: Props) => {
fontFamily: theme.font.normal,
fontSize: '0.875rem',
})}
disabled={disableUnassignButton}
loading={unassignLinodeLoading}
variant="text"
>
Expand Down
Loading
Loading