diff --git a/packages/manager/.changeset/pr-10308-upcoming-features-1711132212593.md b/packages/manager/.changeset/pr-10308-upcoming-features-1711132212593.md new file mode 100644 index 00000000000..e6aab08a4bc --- /dev/null +++ b/packages/manager/.changeset/pr-10308-upcoming-features-1711132212593.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Linode Create Refactor - Part 4 - Access ([#10308](https://github.com/linode/manager/pull/10308)) diff --git a/packages/manager/src/features/Linodes/LinodeCreatev2/Access.test.tsx b/packages/manager/src/features/Linodes/LinodeCreatev2/Access.test.tsx new file mode 100644 index 00000000000..93fe0b6fa6a --- /dev/null +++ b/packages/manager/src/features/Linodes/LinodeCreatev2/Access.test.tsx @@ -0,0 +1,102 @@ +import { waitFor } from '@testing-library/react'; +import React from 'react'; + +import { profileFactory, sshKeyFactory } from 'src/factories'; +import { grantsFactory } from 'src/factories/grants'; +import { makeResourcePage } from 'src/mocks/serverHandlers'; +import { HttpResponse, http, server } from 'src/mocks/testServer'; +import { renderWithThemeAndHookFormContext } from 'src/utilities/testHelpers'; + +import { Access } from './Access'; + +describe('Access', () => { + it('should render a root password input', async () => { + const { findByLabelText } = renderWithThemeAndHookFormContext({ + component: , + }); + + const rootPasswordInput = await findByLabelText('Root Password'); + + expect(rootPasswordInput).toBeVisible(); + expect(rootPasswordInput).toBeEnabled(); + }); + + it('should render a SSH Keys heading', async () => { + const { getAllByText } = renderWithThemeAndHookFormContext({ + component: , + }); + + const heading = getAllByText('SSH Keys')[0]; + + expect(heading).toBeVisible(); + expect(heading.tagName).toBe('H2'); + }); + + it('should render an "Add An SSH Key" button', async () => { + const { getByText } = renderWithThemeAndHookFormContext({ + component: , + }); + + const addSSHKeyButton = getByText('Add an SSH Key'); + + expect(addSSHKeyButton).toBeVisible(); + expect(addSSHKeyButton).toBeEnabled(); + }); + + it('should disable the password input if the user does not have permission to create linodes', async () => { + server.use( + http.get('*/v4/profile', () => { + return HttpResponse.json(profileFactory.build({ restricted: true })); + }), + http.get('*/v4/profile/sshkeys', () => { + return HttpResponse.json(makeResourcePage([])); + }), + http.get('*/v4/profile/grants', () => { + return HttpResponse.json( + grantsFactory.build({ global: { add_linodes: false } }) + ); + }) + ); + + const { findByLabelText } = renderWithThemeAndHookFormContext({ + component: , + }); + + const rootPasswordInput = await findByLabelText('Root Password'); + + await waitFor(() => { + expect(rootPasswordInput).toBeDisabled(); + }); + }); + + it('should disable ssh key selection if the user does not have permission to create linodes', async () => { + const sshKeys = sshKeyFactory.buildList(3); + server.use( + http.get('*/v4/profile', () => { + return HttpResponse.json(profileFactory.build({ restricted: true })); + }), + http.get('*/v4/profile/sshkeys', () => { + return HttpResponse.json(makeResourcePage(sshKeys)); + }), + http.get('*/v4/profile/grants', () => { + return HttpResponse.json( + grantsFactory.build({ global: { add_linodes: false } }) + ); + }) + ); + + const { findByText, getByRole } = renderWithThemeAndHookFormContext({ + component: , + }); + + // Make sure the restricted user's SSH keys are loaded + for (const sshKey of sshKeys) { + // eslint-disable-next-line no-await-in-loop + expect(await findByText(sshKey.label, { exact: false })).toBeVisible(); + } + + await waitFor(() => { + expect(getByRole('checkbox')).toBeDisabled(); + }); + }); +}); diff --git a/packages/manager/src/features/Linodes/LinodeCreatev2/Access.tsx b/packages/manager/src/features/Linodes/LinodeCreatev2/Access.tsx new file mode 100644 index 00000000000..1daa81d6edd --- /dev/null +++ b/packages/manager/src/features/Linodes/LinodeCreatev2/Access.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { Controller, useFormContext } from 'react-hook-form'; + +import UserSSHKeyPanel from 'src/components/AccessPanel/UserSSHKeyPanel'; +import { Divider } from 'src/components/Divider'; +import { Paper } from 'src/components/Paper'; +import { Skeleton } from 'src/components/Skeleton'; +import { inputMaxWidth } from 'src/foundations/themes/light'; +import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck'; + +import type { CreateLinodeRequest } from '@linode/api-v4'; + +const PasswordInput = React.lazy( + () => import('src/components/PasswordInput/PasswordInput') +); + +export const Access = () => { + const { control } = useFormContext(); + + const isLinodeCreateRestricted = useRestrictedGlobalGrantCheck({ + globalGrantType: 'add_linodes', + }); + + return ( + + } + > + ( + + )} + control={control} + name="root_pass" + /> + + + ( + + )} + control={control} + name="authorized_users" + /> + + ); +}; diff --git a/packages/manager/src/features/Linodes/LinodeCreatev2/index.tsx b/packages/manager/src/features/Linodes/LinodeCreatev2/index.tsx index 5bbf1bcf08d..1857a642bdc 100644 --- a/packages/manager/src/features/Linodes/LinodeCreatev2/index.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreatev2/index.tsx @@ -11,6 +11,7 @@ import { TabPanels } from 'src/components/Tabs/TabPanels'; import { Tabs } from 'src/components/Tabs/Tabs'; import { useCreateLinodeMutation } from 'src/queries/linodes/linodes'; +import { Access } from './Access'; import { Details } from './Details/Details'; import { Error } from './Error'; import { Plan } from './Plan'; @@ -58,8 +59,8 @@ export const LinodeCreatev2 = () => { title="Create" />
+ - updateParams({ type: tabs[index] })} @@ -88,6 +89,7 @@ export const LinodeCreatev2 = () => {
+ diff --git a/packages/manager/src/foundations/themes/light.ts b/packages/manager/src/foundations/themes/light.ts index 27cb15df179..0d892766611 100644 --- a/packages/manager/src/foundations/themes/light.ts +++ b/packages/manager/src/foundations/themes/light.ts @@ -3,7 +3,7 @@ import { ThemeOptions } from '@mui/material/styles'; import { breakpoints } from 'src/foundations/breakpoints'; import { latoWeb } from 'src/foundations/fonts'; -const inputMaxWidth = 416; +export const inputMaxWidth = 416; export const bg = { app: '#f4f5f6',