Skip to content

Commit

Permalink
Clean up spacing in login modal in Connect (#48143)
Browse files Browse the repository at this point in the history
* Align ClusterLogin story with actual dialog presentation

* Add stories for LocalError and SsoError

* Share setup code between ClusterLogin and ClusterLoginReason

* Use named Indicator export in design/index.ts

* Align ClusterAdd story with actual presentation

* Clean up spacing within ClusterConnect

* Remove type assertions from storyHelpers.tsx

* Remove bottom margin from login disabled alert
  • Loading branch information
ravicious authored Nov 4, 2024
1 parent 6254c7b commit eb0a427
Show file tree
Hide file tree
Showing 15 changed files with 310 additions and 232 deletions.
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

0 comments on commit eb0a427

Please sign in to comment.