Skip to content

Commit 165b5bf

Browse files
upcoming: [M3-7696] - Edit Access key Drawer - Fix Save button is disabled (linode#10118)
* upcoming: [M3-7696] - Edit Access key Drawer - Save Edit Access key Drawer - Fix save button is disabled. * Remove unused imports * Added changeset: Edit Access key Drawer - Fix Save button is disabled. * code cleanup * Update utils.ts * PR - feedback * PR feedback - @abailly-akamai * Update packages/manager/.changeset/pr-10118-upcoming-features-1706544516803.md Co-authored-by: Dajahi Wiley <[email protected]> * PR - feedback - @DevDW * Remove error when regions are selected. --------- Co-authored-by: Dajahi Wiley <[email protected]>
1 parent e33c125 commit 165b5bf

File tree

11 files changed

+285
-39
lines changed

11 files changed

+285
-39
lines changed

packages/api-v4/src/object-storage/objectStorageKeys.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { createObjectStorageKeysSchema } from '@linode/validation/lib/objectStorageKeys.schema';
1+
import {
2+
createObjectStorageKeysSchema,
3+
updateObjectStorageKeysSchema,
4+
} from '@linode/validation/lib/objectStorageKeys.schema';
25
import { API_ROOT } from '../constants';
36
import Request, {
47
setData,
@@ -51,7 +54,7 @@ export const updateObjectStorageKey = (
5154
Request<ObjectStorageKey>(
5255
setMethod('PUT'),
5356
setURL(`${API_ROOT}/object-storage/keys/${encodeURIComponent(id)}`),
54-
setData(data, createObjectStorageKeysSchema)
57+
setData(data, updateObjectStorageKeysSchema)
5558
);
5659

5760
/**
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+
"Save" button in Edit Access Key drawer disabled unless field values are changed ([#10118](https://github.com/linode/manager/pull/10118))

packages/manager/src/features/Linodes/LinodesCreate/LinodeCreateContainer.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -698,10 +698,10 @@ class LinodeCreateContainer extends React.PureComponent<CombinedProps, State> {
698698
selectedTypeID: this.params.typeID,
699699
showGDPRCheckbox: Boolean(
700700
!this.props.profile.data?.restricted &&
701-
isEURegion(
702-
getSelectedRegionGroup(this.props.regionsData, this.params.regionID)
703-
) &&
704-
this.props.agreements?.data?.eu_model
701+
isEURegion(
702+
getSelectedRegionGroup(this.props.regionsData, this.params.regionID)
703+
) &&
704+
this.props.agreements?.data?.eu_model
705705
),
706706
signedAgreement: false,
707707
};
@@ -833,10 +833,10 @@ class LinodeCreateContainer extends React.PureComponent<CombinedProps, State> {
833833
const request =
834834
createType === 'fromLinode'
835835
? () =>
836-
this.props.linodeActions.cloneLinode({
837-
sourceLinodeId: linodeID!,
838-
...payload,
839-
})
836+
this.props.linodeActions.cloneLinode({
837+
sourceLinodeId: linodeID!,
838+
...payload,
839+
})
840840
: () => this.props.linodeActions.createLinode(payload);
841841

842842
this.setState({ formIsSubmitting: true });

packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyDrawer.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
Scope,
77
} from '@linode/api-v4/lib/object-storage';
88
import { createObjectStorageKeysSchema } from '@linode/validation/lib/objectStorageKeys.schema';
9-
import { Formik } from 'formik';
9+
import { Formik, FormikProps } from 'formik';
1010
import * as React from 'react';
1111

1212
import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel';
@@ -33,7 +33,10 @@ export interface AccessKeyDrawerProps {
3333
// If the mode is 'editing', we should have an ObjectStorageKey to edit
3434
objectStorageKey?: ObjectStorageKey;
3535
onClose: () => void;
36-
onSubmit: (values: ObjectStorageKeyRequest, formikProps: any) => void;
36+
onSubmit: (
37+
values: ObjectStorageKeyRequest,
38+
formikProps: FormikProps<ObjectStorageKeyRequest>
39+
) => void;
3740
open: boolean;
3841
}
3942

@@ -120,7 +123,10 @@ export const AccessKeyDrawer = (props: AccessKeyDrawerProps) => {
120123
label: initialLabelValue,
121124
};
122125

123-
const handleSubmit = (values: ObjectStorageKeyRequest, formikProps: any) => {
126+
const handleSubmit = (
127+
values: ObjectStorageKeyRequest,
128+
formikProps: FormikProps<ObjectStorageKeyRequest>
129+
) => {
124130
// If the user hasn't toggled the Limited Access button,
125131
// don't include any bucket_access information in the payload.
126132

packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyLanding.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
revokeObjectStorageKey,
66
updateObjectStorageKey,
77
} from '@linode/api-v4/lib/object-storage';
8-
import { FormikBag } from 'formik';
8+
import { FormikBag, FormikHelpers } from 'formik';
99
import * as React from 'react';
1010

1111
import { DocumentTitleSegment } from 'src/components/DocumentTitle';
@@ -96,7 +96,11 @@ export const AccessKeyLanding = (props: Props) => {
9696

9797
const handleCreateKey = (
9898
values: ObjectStorageKeyRequest,
99-
{ setErrors, setStatus, setSubmitting }: FormikProps
99+
{
100+
setErrors,
101+
setStatus,
102+
setSubmitting,
103+
}: FormikHelpers<ObjectStorageKeyRequest>
100104
) => {
101105
// Clear out status (used for general errors)
102106
setStatus(null);
@@ -152,7 +156,11 @@ export const AccessKeyLanding = (props: Props) => {
152156

153157
const handleEditKey = (
154158
values: ObjectStorageKeyRequest,
155-
{ setErrors, setStatus, setSubmitting }: FormikProps
159+
{
160+
setErrors,
161+
setStatus,
162+
setSubmitting,
163+
}: FormikHelpers<ObjectStorageKeyRequest>
156164
) => {
157165
// This shouldn't happen, but just in case.
158166
if (!keyToEdit) {
@@ -170,7 +178,7 @@ export const AccessKeyLanding = (props: Props) => {
170178

171179
setSubmitting(true);
172180

173-
updateObjectStorageKey(keyToEdit.id, { label: values.label })
181+
updateObjectStorageKey(keyToEdit.id, values)
174182
.then((_) => {
175183
setSubmitting(false);
176184

packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyTable/AccessKeyActionMenu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const AccessKeyActionMenu = ({
3838
onClick: () => {
3939
openDrawer('editing', objectStorageKey);
4040
},
41-
title: 'Edit Label',
41+
title: isObjMultiClusterEnabled ? 'Edit' : 'Edit Label',
4242
},
4343
{
4444
onClick: () => {

packages/manager/src/features/ObjectStorage/AccessKeyLanding/OMC_AccessKeyDrawer.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import {
55
ObjectStorageKey,
66
ObjectStorageKeyRequest,
77
Scope,
8+
UpdateObjectStorageKeyRequest,
89
} from '@linode/api-v4/lib/object-storage';
910
import { createObjectStorageKeysSchema } from '@linode/validation/lib/objectStorageKeys.schema';
10-
import { useFormik } from 'formik';
11+
import { useFormik, FormikProps } from 'formik';
1112
import React, { useEffect, useState } from 'react';
1213

1314
import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel';
@@ -28,14 +29,20 @@ import { confirmObjectStorage } from '../utilities';
2829
import { AccessKeyRegions } from './AccessKeyRegions/AccessKeyRegions';
2930
import { LimitedAccessControls } from './LimitedAccessControls';
3031
import { MODE } from './types';
32+
import { generateUpdatePayload, hasLabelOrRegionsChanged } from './utils';
3133

3234
export interface AccessKeyDrawerProps {
3335
isRestrictedUser: boolean;
3436
mode: MODE;
3537
// If the mode is 'editing', we should have an ObjectStorageKey to edit
3638
objectStorageKey?: ObjectStorageKey;
3739
onClose: () => void;
38-
onSubmit: (values: ObjectStorageKeyRequest, formikProps: any) => void;
40+
onSubmit: (
41+
values: ObjectStorageKeyRequest | UpdateObjectStorageKeyRequest,
42+
formikProps: FormikProps<
43+
ObjectStorageKeyRequest | UpdateObjectStorageKeyRequest
44+
>
45+
) => void;
3946
open: boolean;
4047
}
4148

@@ -116,10 +123,11 @@ export const OMC_AccessKeyDrawer = (props: AccessKeyDrawerProps) => {
116123
// and so not included in Formik's types
117124
const [limitedAccessChecked, setLimitedAccessChecked] = useState(false);
118125

119-
const title = createMode ? 'Create Access Key' : 'Edit Access Key Label';
126+
const title = createMode ? 'Create Access Key' : 'Edit Access Key';
120127

121128
const initialLabelValue =
122129
!createMode && objectStorageKey ? objectStorageKey.label : '';
130+
123131
const initialRegions =
124132
!createMode && objectStorageKey
125133
? objectStorageKey.regions?.map((region) => region.id)
@@ -148,13 +156,25 @@ export const OMC_AccessKeyDrawer = (props: AccessKeyDrawerProps) => {
148156
}
149157
: { ...values, bucket_access: null };
150158

151-
onSubmit(payload, formik);
159+
const updatePayload = generateUpdatePayload(values, initialValues);
160+
161+
if (mode !== 'creating') {
162+
onSubmit(updatePayload, formik);
163+
} else {
164+
onSubmit(payload, formik);
165+
}
152166
},
153167
validateOnBlur: true,
154168
validateOnChange: false,
155169
validationSchema: createObjectStorageKeysSchema,
156170
});
157171

172+
const isSaveDisabled =
173+
isRestrictedUser ||
174+
(mode !== 'creating' &&
175+
objectStorageKey &&
176+
!hasLabelOrRegionsChanged(formik.values, objectStorageKey));
177+
158178
const beforeSubmit = () => {
159179
confirmObjectStorage<FormState>(
160180
accountSettings?.object_storage || 'active',
@@ -257,6 +277,7 @@ export const OMC_AccessKeyDrawer = (props: AccessKeyDrawerProps) => {
257277
'bucket_access',
258278
getDefaultScopes(bucketsInRegions, regionsLookup)
259279
);
280+
formik.validateField('regions');
260281
}}
261282
onChange={(values) => {
262283
const bucketsInRegions = buckets?.filter(
@@ -287,10 +308,7 @@ export const OMC_AccessKeyDrawer = (props: AccessKeyDrawerProps) => {
287308
<ActionsPanel
288309
primaryButtonProps={{
289310
'data-testid': 'submit',
290-
disabled:
291-
isRestrictedUser ||
292-
(mode !== 'creating' &&
293-
formik.values.label === initialLabelValue),
311+
disabled: isSaveDisabled,
294312
label: createMode ? 'Create Access Key' : 'Save Changes',
295313
loading: formik.isSubmitting,
296314
onClick: beforeSubmit,
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { ObjectStorageKey } from '@linode/api-v4/lib/object-storage';
2+
3+
import { FormState } from './OMC_AccessKeyDrawer';
4+
import { generateUpdatePayload, hasLabelOrRegionsChanged } from './utils';
5+
6+
describe('generateUpdatePayload', () => {
7+
const initialValues: FormState = {
8+
bucket_access: [],
9+
label: 'initialLabel',
10+
regions: ['region1', 'region2'],
11+
};
12+
13+
it('should return empty object if no changes', () => {
14+
const updatedValues = { ...initialValues };
15+
expect(generateUpdatePayload(updatedValues, initialValues)).toEqual({});
16+
});
17+
18+
it('should return updated label if only label changed', () => {
19+
const updatedValues = { ...initialValues, label: 'newLabel' };
20+
expect(generateUpdatePayload(updatedValues, initialValues)).toEqual({
21+
label: 'newLabel',
22+
});
23+
});
24+
25+
it('should return updated regions if only regions changed', () => {
26+
const updatedValues = { ...initialValues, regions: ['region3', 'region4'] };
27+
expect(generateUpdatePayload(updatedValues, initialValues)).toEqual({
28+
regions: ['region3', 'region4'],
29+
});
30+
});
31+
32+
it('should return updated label and regions if both changed', () => {
33+
const updatedValues = {
34+
bucket_access: [],
35+
label: 'newLabel',
36+
regions: ['region3', 'region4'],
37+
};
38+
expect(generateUpdatePayload(updatedValues, initialValues)).toEqual({
39+
label: 'newLabel',
40+
regions: ['region3', 'region4'],
41+
});
42+
});
43+
});
44+
45+
describe('hasLabelOrRegionsChanged', () => {
46+
const updatedValues: FormState = {
47+
bucket_access: [],
48+
label: 'initialLabel',
49+
regions: ['region3', 'region4'],
50+
};
51+
const initialValues: ObjectStorageKey = {
52+
access_key: '',
53+
bucket_access: null,
54+
id: 0,
55+
label: updatedValues.label,
56+
limited: false,
57+
regions: [
58+
{ id: 'region3', s3_endpoint: '' },
59+
{ id: 'region4', s3_endpoint: '' },
60+
],
61+
62+
secret_key: '',
63+
};
64+
65+
it('returns false when both label and regions are unchanged', () => {
66+
expect(hasLabelOrRegionsChanged(updatedValues, initialValues)).toBe(false);
67+
});
68+
69+
it('returns true when only the label has changed', () => {
70+
expect(
71+
hasLabelOrRegionsChanged(
72+
{ ...updatedValues, label: 'newLabel' },
73+
initialValues
74+
)
75+
).toBe(true);
76+
});
77+
78+
it('returns true when only the regions have changed', () => {
79+
expect(
80+
hasLabelOrRegionsChanged(
81+
{
82+
...updatedValues,
83+
regions: ['region5'],
84+
},
85+
initialValues
86+
)
87+
).toBe(true);
88+
});
89+
90+
it('returns true when both label and regions have changed', () => {
91+
expect(
92+
hasLabelOrRegionsChanged(
93+
{ ...updatedValues, label: 'newLabel', regions: ['region5'] },
94+
initialValues
95+
)
96+
).toBe(true);
97+
});
98+
});

0 commit comments

Comments
 (0)