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

Clean up spacing in login modal in Connect #48143

Merged
merged 8 commits into from
Nov 4, 2024
2 changes: 1 addition & 1 deletion web/packages/design/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import ButtonLink from './ButtonLink';
import { ButtonWithMenu } from './ButtonWithMenu';
import Card from './Card';
import CardSuccess, { CardSuccessLogin } from './CardSuccess';
import Indicator from './Indicator';
import { Indicator } from './Indicator';
import Input from './Input';
import Label from './Label';
import LabelInput from './LabelInput';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React from 'react';
import React, { PropsWithChildren } from 'react';

import Dialog from 'design/Dialog';

import { MockAppContextProvider } from 'teleterm/ui/fixtures/MockAppContextProvider';
import { MockAppContext } from 'teleterm/ui/fixtures/mocks';
import { makeRootCluster } from 'teleterm/services/tshd/testHelpers';

import { dialogCss } from '../spacing';

import { ClusterAdd } from './ClusterAdd';

import type * as tshd from 'teleterm/services/tshd/types';
Expand All @@ -33,23 +37,27 @@ export default {
export const Story = () => {
return (
<MockAppContextProvider appContext={getMockAppContext()}>
<ClusterAdd
prefill={{ clusterAddress: undefined }}
onSuccess={() => {}}
onCancel={() => {}}
/>
<Wrapper>
<ClusterAdd
prefill={{ clusterAddress: undefined }}
onSuccess={() => {}}
onCancel={() => {}}
/>
</Wrapper>
</MockAppContextProvider>
);
};

export const WithPrefill = () => {
return (
<MockAppContextProvider appContext={getMockAppContext()}>
<ClusterAdd
prefill={{ clusterAddress: 'foo.example.com:3080' }}
onSuccess={() => {}}
onCancel={() => {}}
/>
<Wrapper>
<ClusterAdd
prefill={{ clusterAddress: 'foo.example.com:3080' }}
onSuccess={() => {}}
onCancel={() => {}}
/>
</Wrapper>
</MockAppContextProvider>
);
};
Expand All @@ -62,11 +70,13 @@ export const ErrorOnSubmit = () => {
Promise.reject(new Error('Oops, something went wrong.')),
})}
>
<ClusterAdd
prefill={{ clusterAddress: undefined }}
onSuccess={() => {}}
onCancel={() => {}}
/>
<Wrapper>
<ClusterAdd
prefill={{ clusterAddress: undefined }}
onSuccess={() => {}}
onCancel={() => {}}
/>
</Wrapper>
</MockAppContextProvider>
);
};
Expand All @@ -81,3 +91,9 @@ function getMockAppContext(
args.addRootCluster || (() => Promise.resolve(makeRootCluster()));
return appContext;
}

const Wrapper = ({ children }: PropsWithChildren) => (
<Dialog dialogCss={dialogCss} open>
{children}
</Dialog>
);
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React, { useState } from 'react';
import { useState } from 'react';
import * as Alerts from 'design/Alert';
import { Box, ButtonPrimary, ButtonSecondary, H2 } from 'design';
import { Box, Flex, ButtonPrimary, ButtonSecondary, H2 } from 'design';
import FieldInput from 'shared/components/FieldInput';
import Validation from 'shared/components/Validation';
import { requiredField } from 'shared/components/Validation/rules';
Expand All @@ -27,6 +27,8 @@ import { useAsync } from 'shared/hooks/useAsync';

import { useAppContext } from 'teleterm/ui/appContextProvider';

import { outermostPadding } from '../spacing';

export function ClusterAdd(props: {
onCancel(): void;
onSuccess(clusterUri: string): void;
Expand All @@ -43,7 +45,7 @@ export function ClusterAdd(props: {
const [addr, setAddr] = useState(props.prefill.clusterAddress || '');

return (
<Box p={4}>
<Box px={outermostPadding}>
<Validation>
{({ validator }) => (
<form
Expand All @@ -55,25 +57,22 @@ export function ClusterAdd(props: {
<DialogHeader>
<H2>Enter cluster address</H2>
</DialogHeader>
<DialogContent mb={2}>
<DialogContent mb={0} gap={3}>
{status === 'error' && (
<Alerts.Danger mb={5} details={statusText}>
<Alerts.Danger mb={0} details={statusText}>
Could not add the cluster
</Alerts.Danger>
)}
<FieldInput
rule={requiredField('Cluster address is required')}
value={addr}
autoFocus
mb={0}
onChange={e => setAddr(e.target.value)}
placeholder="teleport.example.com"
/>
<Box mt="5">
<ButtonPrimary
disabled={status === 'processing'}
mr="3"
type="submit"
>
<Flex gap={3}>
<ButtonPrimary disabled={status === 'processing'} type="submit">
Next
</ButtonPrimary>
<ButtonSecondary
Expand All @@ -86,7 +85,7 @@ export function ClusterAdd(props: {
>
Cancel
</ButtonSecondary>
</Box>
</Flex>
</DialogContent>
</form>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React, { useState } from 'react';
import { useState } from 'react';

import Dialog from 'design/Dialog';

Expand All @@ -26,6 +26,7 @@ import { RootClusterUri } from 'teleterm/ui/uri';

import { ClusterAdd } from './ClusterAdd';
import { ClusterLogin } from './ClusterLogin';
import { dialogCss } from './spacing';

export function ClusterConnect(props: { dialog: DialogClusterConnect }) {
const [createdClusterUri, setCreatedClusterUri] = useState<
Expand All @@ -45,11 +46,7 @@ export function ClusterConnect(props: { dialog: DialogClusterConnect }) {

return (
<Dialog
dialogCss={() => ({
maxWidth: '480px',
width: '100%',
padding: '0',
})}
dialogCss={dialogCss}
disableEscapeKeyDown={false}
onClose={props.dialog.onCancel}
open={true}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,62 +15,15 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { makeErrorAttempt, makeProcessingAttempt } from 'shared/hooks/useAsync';

import { FC, PropsWithChildren } from 'react';

import { Box } from 'design';
import {
Attempt,
makeErrorAttempt,
makeProcessingAttempt,
} from 'shared/hooks/useAsync';

import * as types from 'teleterm/ui/services/clusters/types';

import {
ClusterLoginPresentation,
ClusterLoginPresentationProps,
} from './ClusterLogin';
import { ClusterLoginPresentation } from './ClusterLogin';
import { TestContainer, makeProps } from './storyHelpers';

export default {
title: 'Teleterm/ModalsHost/ClusterLogin',
};

function makeProps(): ClusterLoginPresentationProps {
return {
shouldPromptSsoStatus: false,
title: 'localhost',
loginAttempt: {
status: '',
statusText: '',
} as Attempt<void>,
init: () => null,
initAttempt: {
status: 'success',
statusText: '',
data: {
localAuthEnabled: true,
authProviders: [],
type: '',
hasMessageOfTheDay: false,
allowPasswordless: true,
localConnectorName: '',
authType: 'local',
} as types.AuthSettings,
} as const,

loggedInUserName: null,
onCloseDialog: () => null,
onAbort: () => null,
onLoginWithLocal: () => Promise.resolve<[void, Error]>([null, null]),
onLoginWithPasswordless: () => Promise.resolve<[void, Error]>([null, null]),
onLoginWithSso: () => null,
clearLoginAttempt: () => null,
passwordlessLoginState: null,
reason: undefined,
};
}

export const LocalOnly = () => {
const props = makeProps();
props.initAttempt.data.allowPasswordless = false;
Expand Down Expand Up @@ -131,6 +84,19 @@ export const LocalProcessing = () => {
);
};

export const LocalError = () => {
const props = makeProps();
props.initAttempt.data.allowPasswordless = false;
props.loginAttempt = makeErrorAttempt(new Error('invalid credentials'));
props.loggedInUserName = 'alice';

return (
<TestContainer>
<ClusterLoginPresentation {...props} />
</TestContainer>
);
};

const authProviders = [
{ type: 'github', name: 'github', displayName: 'GitHub' },
{ type: 'saml', name: 'microsoft', displayName: 'Microsoft' },
Expand All @@ -149,6 +115,32 @@ export const SsoOnly = () => {
);
};

export const SsoPrompt = () => {
const props = makeProps();
props.loginAttempt.status = 'processing';
props.shouldPromptSsoStatus = true;
return (
<TestContainer>
<ClusterLoginPresentation {...props} />
</TestContainer>
);
};

export const SsoError = () => {
const props = makeProps();
props.initAttempt.data.localAuthEnabled = false;
props.initAttempt.data.authType = 'github';
props.initAttempt.data.authProviders = authProviders;
props.loginAttempt = makeErrorAttempt(
new Error("Failed to log in. Please check Teleport's log for more details.")
);
return (
<TestContainer>
<ClusterLoginPresentation {...props} />
</TestContainer>
);
};

export const LocalWithPasswordless = () => {
return (
<TestContainer>
Expand Down Expand Up @@ -308,28 +300,3 @@ export const HardwareCredentialPromptProcessing = () => {
</TestContainer>
);
};
export const SsoPrompt = () => {
const props = makeProps();
props.loginAttempt.status = 'processing';
props.shouldPromptSsoStatus = true;
return (
<TestContainer>
<ClusterLoginPresentation {...props} />
</TestContainer>
);
};

const TestContainer: FC<PropsWithChildren> = ({ children }) => (
<>
<span>Bordered box is not part of the component</span>
<Box
css={`
width: 450px;
border: 1px solid ${props => props.theme.colors.levels.elevated};
background: ${props => props.theme.colors.levels.surface};
`}
>
{children}
</Box>
</>
);
Loading
Loading