convert plugin 'sandbox's UI to use ant.design
Summary:
Bootcamp task:
I’m removing the remaining UI components imported from ‘flipper’ and replacing with new ones from ‘antd’.
I’m also attempting to polish 🇵🇱 the UI and UX of the plugin:
UI:
- Selecting one of the listed sandboxes and custom URL lead to the same action -> align all components together
UX:
- Sometimes loading sandboxes takes time, and it’s not clear for the user what’s happening -> add a loading indicator
- At first the input field was a bit confusing to me -> add more labels to explain the purpose of visible components
- It’s not always clear what happened after requesting a sandbox change -> the result of all actions is now confirmed with the small 'popup' notification.
I don't have any experience with web frontend, so if something is terribly wrong below - don't be afraid to point it out :)
Before/After:
{F745958473}
Reviewed By: mweststrate
Differential Revision: D37343946
fbshipit-source-id: 3dbdd213ffd5540dc7a418c1590eb956ef4c6715
This commit is contained in:
committed by
Facebook GitHub Bot
parent
335dec1a08
commit
8e784f2579
@@ -7,9 +7,15 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {FlexColumn, ButtonGroup, Button, styled, colors} from 'flipper';
|
||||
import React, {ChangeEvent} from 'react';
|
||||
import {PluginClient, createState, usePlugin, useValue} from 'flipper-plugin';
|
||||
import {Button, Input, Typography, Spin, message} from 'antd';
|
||||
import {
|
||||
PluginClient,
|
||||
createState,
|
||||
usePlugin,
|
||||
useValue,
|
||||
Layout,
|
||||
} from 'flipper-plugin';
|
||||
|
||||
export type Sandbox = {
|
||||
name: string;
|
||||
@@ -23,55 +29,24 @@ type ClientMethods = {
|
||||
|
||||
type SetSandboxResult = {result: boolean};
|
||||
|
||||
const BigButton = styled(Button)({
|
||||
flexGrow: 1,
|
||||
fontSize: 24,
|
||||
padding: 20,
|
||||
});
|
||||
|
||||
const ButtonContainer = styled(FlexColumn)({
|
||||
alignItems: 'center',
|
||||
padding: 20,
|
||||
});
|
||||
|
||||
const TextInput = styled.input({
|
||||
border: `1px solid ${colors.light10}`,
|
||||
fontSize: '1em',
|
||||
padding: '0 5px',
|
||||
borderRight: 0,
|
||||
borderTopLeftRadius: 4,
|
||||
borderBottomLeftRadius: 4,
|
||||
flexGrow: 1,
|
||||
});
|
||||
|
||||
const FeedbackMessage = styled.span({
|
||||
fontSize: '1.2em',
|
||||
paddingTop: '10px',
|
||||
color: 'green',
|
||||
});
|
||||
|
||||
const TextInputLayout = styled(FlexColumn)({
|
||||
float: 'left',
|
||||
justifyContent: 'center',
|
||||
flexGrow: 1,
|
||||
borderRadius: 4,
|
||||
marginRight: 15,
|
||||
marginTop: 15,
|
||||
marginLeft: 15,
|
||||
});
|
||||
|
||||
export function plugin(client: PluginClient<{}, ClientMethods>) {
|
||||
const sandboxes = createState<Array<Sandbox>>([]);
|
||||
const customSandbox = createState<string>('');
|
||||
const showFeedback = createState<boolean>(false);
|
||||
const isLoadingSandboxes = createState<boolean>(false);
|
||||
|
||||
client.onConnect(() => {
|
||||
isLoadingSandboxes.set(true);
|
||||
client
|
||||
.send('getSandbox', undefined)
|
||||
.then((results: Array<Sandbox>) => {
|
||||
sandboxes.set(results);
|
||||
isLoadingSandboxes.set(false);
|
||||
})
|
||||
.catch((e) => console.error('[sandbox] getSandbox call failed:', e));
|
||||
.catch((e) => {
|
||||
console.error('[sandbox] getSandbox call failed:', e);
|
||||
isLoadingSandboxes.set(false);
|
||||
displayError(e);
|
||||
});
|
||||
});
|
||||
|
||||
const onSendSandboxEnvironment = (sandbox: string) => {
|
||||
@@ -80,25 +55,35 @@ export function plugin(client: PluginClient<{}, ClientMethods>) {
|
||||
sandbox,
|
||||
})
|
||||
.then((result: SetSandboxResult) => {
|
||||
setTimeout(() => {
|
||||
showFeedback.set(false);
|
||||
}, 3000);
|
||||
showFeedback.set(result.result);
|
||||
if (result.result)
|
||||
displaySuccess('Update to ' + sandbox + ' successful');
|
||||
else displayError('Update to ' + sandbox + ' failed');
|
||||
})
|
||||
.catch((e) => console.error('[sandbox] setSandbox call failed:', e));
|
||||
.catch((e) => {
|
||||
console.error('[sandbox] setSandbox call failed:', e);
|
||||
displayError(e);
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeSandbox = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
customSandbox.set(e.target.value);
|
||||
};
|
||||
|
||||
const displaySuccess = (title: string) => {
|
||||
message.success(title);
|
||||
};
|
||||
|
||||
const displayError = (title: string) => {
|
||||
message.error(title);
|
||||
};
|
||||
|
||||
return {
|
||||
client,
|
||||
onChangeSandbox,
|
||||
onSendSandboxEnvironment,
|
||||
customSandbox,
|
||||
sandboxes,
|
||||
showFeedback,
|
||||
isLoadingSandboxes,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -106,16 +91,39 @@ export function Component() {
|
||||
const instance = usePlugin(plugin);
|
||||
const customSandbox = useValue(instance.customSandbox);
|
||||
const sandboxes = useValue(instance.sandboxes);
|
||||
const showFeedback = useValue(instance.showFeedback);
|
||||
const isLoadingSandboxes = useValue(instance.isLoadingSandboxes);
|
||||
|
||||
return (
|
||||
<FlexColumn>
|
||||
<TextInputLayout>
|
||||
<ButtonGroup>
|
||||
<TextInput
|
||||
type="text"
|
||||
placeholder="Sandbox URL (e.g. unixname.sb.facebook.com)"
|
||||
key="sandbox-url"
|
||||
<Layout.Container center pad="medium">
|
||||
<Layout.Container
|
||||
center
|
||||
gap
|
||||
style={{
|
||||
width: '350px',
|
||||
}}>
|
||||
<Typography.Text type="secondary">
|
||||
Select the environment:
|
||||
</Typography.Text>
|
||||
<Spin spinning={isLoadingSandboxes} />
|
||||
{sandboxes.map((sandbox) => (
|
||||
<Button
|
||||
key={sandbox.value}
|
||||
onClick={() => instance.onSendSandboxEnvironment(sandbox.value)}
|
||||
style={{
|
||||
width: '100%',
|
||||
}}>
|
||||
{sandbox.name}
|
||||
</Button>
|
||||
))}
|
||||
<Typography.Text type="secondary">
|
||||
Provide custom Sandbox URL
|
||||
</Typography.Text>
|
||||
<Input.Group compact>
|
||||
<Input
|
||||
style={{
|
||||
width: 'calc(100% - 80px)',
|
||||
}}
|
||||
placeholder="e.g. unixname.sb.facebook.com"
|
||||
onChange={instance.onChangeSandbox}
|
||||
onKeyPress={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
@@ -124,24 +132,13 @@ export function Component() {
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
key="sandbox-send"
|
||||
icon="download"
|
||||
type="primary"
|
||||
onClick={() => instance.onSendSandboxEnvironment(customSandbox)}
|
||||
disabled={customSandbox == null}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
<FeedbackMessage hidden={showFeedback == false}>
|
||||
Success!
|
||||
</FeedbackMessage>
|
||||
</TextInputLayout>
|
||||
{sandboxes.map((sandbox) => (
|
||||
<ButtonContainer key={sandbox.value}>
|
||||
<BigButton
|
||||
onClick={() => instance.onSendSandboxEnvironment(sandbox.value)}>
|
||||
{sandbox.name}
|
||||
</BigButton>
|
||||
</ButtonContainer>
|
||||
))}
|
||||
</FlexColumn>
|
||||
disabled={customSandbox == null}>
|
||||
Submit
|
||||
</Button>
|
||||
</Input.Group>
|
||||
</Layout.Container>
|
||||
</Layout.Container>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user