From f818d0be317c149c6a7f7ce9b374be299fdaf386 Mon Sep 17 00:00:00 2001 From: Zhiwei Date: Tue, 7 Jan 2025 14:03:52 -0500 Subject: [PATCH] fix: [PDI-3099] - Enable `Create Token` button if all visible permissions selected (#11453) * fix: Enable `Create Token` button if all visible perms selected * Added changeset: `Create Token` button becomes disabled when all permissions are selected individually (without using 'select all') and child-account is hidden * Update the in-line doc --- .../pr-11453-fixed-1734765509208.md | 5 +++++ .../APITokens/CreateAPITokenDrawer.tsx | 10 +++++++--- .../features/Profile/APITokens/utils.test.ts | 12 +++++++++++ .../src/features/Profile/APITokens/utils.ts | 20 ++++++++++++------- 4 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 packages/manager/.changeset/pr-11453-fixed-1734765509208.md diff --git a/packages/manager/.changeset/pr-11453-fixed-1734765509208.md b/packages/manager/.changeset/pr-11453-fixed-1734765509208.md new file mode 100644 index 00000000000..07f505ebe8d --- /dev/null +++ b/packages/manager/.changeset/pr-11453-fixed-1734765509208.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +`Create Token` button becomes disabled when all permissions are selected individually (without using 'select all') and child-account is hidden ([#11453](https://github.com/linode/manager/pull/11453)) diff --git a/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.tsx b/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.tsx index e5f00984b76..6d314b46413 100644 --- a/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.tsx +++ b/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.tsx @@ -35,6 +35,7 @@ import { allScopesAreTheSame, basePermNameMap, hasAccessBeenSelectedForAllScopes, + levelMap, permTuplesToScopeString, scopeStringToPermTuples, } from './utils'; @@ -178,8 +179,8 @@ export const CreateAPITokenDrawer = (props: Props) => { // Permission scopes with a different default when Selecting All for the specified access level. const excludedScopesFromSelectAll: ExcludedScope[] = [ { - defaultAccessLevel: 0, - invalidAccessLevels: [1], + defaultAccessLevel: levelMap.none, + invalidAccessLevels: [levelMap.read_only], name: 'vpc', }, ]; @@ -383,7 +384,10 @@ export const CreateAPITokenDrawer = (props: Props) => { form.handleSubmit(), diff --git a/packages/manager/src/features/Profile/APITokens/utils.test.ts b/packages/manager/src/features/Profile/APITokens/utils.test.ts index 634baa93c5c..97e15da7928 100644 --- a/packages/manager/src/features/Profile/APITokens/utils.test.ts +++ b/packages/manager/src/features/Profile/APITokens/utils.test.ts @@ -436,6 +436,11 @@ describe('hasAccessBeenSelectedForAllScopes', () => { ['vpc', 0], ]; + const allExceptChildAccountSelectedScopes: Permission[] = [ + ...allSelectedScopes, + ['child_account', -1], + ]; + it('should return false if scopes are all set to a default of no selection', () => { expect(hasAccessBeenSelectedForAllScopes(defaultScopes)).toBe(false); }); @@ -447,4 +452,11 @@ describe('hasAccessBeenSelectedForAllScopes', () => { it('should return true if all scopes have a valid selection', () => { expect(hasAccessBeenSelectedForAllScopes(allSelectedScopes)).toBe(true); }); + it('should return true if all scopes except those excluded have a valid selection', () => { + expect( + hasAccessBeenSelectedForAllScopes(allExceptChildAccountSelectedScopes, [ + 'child_account', + ]) + ).toBe(true); + }); }); diff --git a/packages/manager/src/features/Profile/APITokens/utils.ts b/packages/manager/src/features/Profile/APITokens/utils.ts index 6ba792065e4..c8bbcb0d6f2 100644 --- a/packages/manager/src/features/Profile/APITokens/utils.ts +++ b/packages/manager/src/features/Profile/APITokens/utils.ts @@ -2,9 +2,7 @@ import { DateTime } from 'luxon'; import { isPast } from 'src/utilities/isPast'; -import { ExcludedScope } from './CreateAPITokenDrawer'; - -export type Permission = [keyof typeof basePermNameMap, number]; +import type { ExcludedScope } from './CreateAPITokenDrawer'; export const basePerms = [ 'account', @@ -46,6 +44,10 @@ export const basePermNameMap = { vpc: 'VPCs', } as const; +type PermissionKey = keyof typeof basePermNameMap; + +export type Permission = [PermissionKey, number]; + export const inverseLevelMap = ['none', 'read_only', 'read_write']; export const levelMap = { @@ -177,7 +179,7 @@ export const permTuplesToScopeString = (scopeTups: Permission[]): string => { } const joinedTups = scopeTups.reduce((acc, [key, value]) => { const level = inverseLevelMap[value]; - if (level !== 'none') { + if (level && level !== 'none') { return [...acc, [key, level].join(':')]; } return [...acc]; @@ -257,17 +259,21 @@ Omit => { * Determines whether a selection has been made for every scope, since by default, the scope permissions are set to null. * * @param scopeTuples - The array of scope tuples. + * @param excludedPerms - The permission keys to be excluded from this check. * @returns {boolean} True if all scopes have permissions set to none/read_only/read_write, false otherwise. */ export const hasAccessBeenSelectedForAllScopes = ( - scopeTuples: Permission[] + scopeTuples: Permission[], + excludedPerms?: PermissionKey[] ): boolean => { const validAccessLevels = [ levelMap['none'], levelMap['read_only'], levelMap['read_write'], ]; - return scopeTuples.every((scopeTuple) => - validAccessLevels.includes(scopeTuple[1]) + return scopeTuples.every( + (scopeTuple) => + validAccessLevels.includes(scopeTuple[1]) || + excludedPerms?.includes(scopeTuple[0]) ); };