Refactor Plugin Selection sheet and add snapshot test

Summary:
This diff fixes a UI bug in export data sheet where the plugin names are stuck to the left and also the bottom buttons are stuck together with no padding. I have also added snapshot test for the same. In order to write test and reduce the complexity I changed the `ExportDataPluginSheet`'s connect method.

Bug:

{F231521086}

Reviewed By: mweststrate

Differential Revision: D20459692

fbshipit-source-id: 1047d6b38738691d682ad6e4ccec45c05e14cbbe
This commit is contained in:
Pritesh Nandgaonkar
2020-03-16 05:20:11 -07:00
committed by Facebook GitHub Bot
parent 18915ba43c
commit f889dc5e40
4 changed files with 424 additions and 34 deletions

View File

@@ -25,7 +25,7 @@ import {
import ListView from './ListView';
import {Dispatch, Action} from 'redux';
import {unsetShare} from '../reducers/application';
import {FlexColumn, styled} from 'flipper';
import {FlexColumn, styled} from '../ui';
import Client from '../Client';
type OwnProps = {
@@ -34,10 +34,8 @@ type OwnProps = {
type StateFromProps = {
share: ShareType | null;
plugins: PluginState;
pluginStates: PluginStatesState;
pluginMessageQueue: PluginMessageQueueState;
selectedClient: Client | undefined;
selectedPlugins: Array<string>;
availablePluginsToExport: Array<{id: string; label: string}>;
};
type DispatchFromProps = {
@@ -58,25 +56,7 @@ const Container = styled(FlexColumn)({
padding: 8,
});
type State = {
availablePluginsToExport: Array<{id: string; label: string}>;
};
class ExportDataPluginSheet extends Component<Props, State> {
state: State = {availablePluginsToExport: []};
static getDerivedStateFromProps(props: Props, _state: State): State {
const {plugins, pluginStates, pluginMessageQueue, selectedClient} = props;
const availablePluginsToExport = getActivePersistentPlugins(
pluginStates,
pluginMessageQueue,
plugins,
selectedClient,
);
return {
availablePluginsToExport,
};
}
class ExportDataPluginSheet extends Component<Props, {}> {
render() {
const {onHide} = this.props;
const onHideWithUnsettingShare = () => {
@@ -88,6 +68,7 @@ class ExportDataPluginSheet extends Component<Props, State> {
<ListView
type="multiple"
title="Select the plugins for which you want to export the data"
leftPadding={8}
onSubmit={() => {
const {share} = this.props;
if (!share) {
@@ -116,8 +97,8 @@ class ExportDataPluginSheet extends Component<Props, State> {
onChange={selectedArray => {
this.props.setSelectedPlugins(selectedArray);
}}
elements={this.state.availablePluginsToExport}
selectedElements={new Set(this.props.plugins.selectedPlugins)}
elements={this.props.availablePluginsToExport}
selectedElements={new Set(this.props.selectedPlugins)}
onHide={onHideWithUnsettingShare}
/>
</Container>
@@ -136,12 +117,16 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
const selectedClient = clients.find(o => {
return o.id === selectedApp;
});
return {
share,
plugins,
const availablePluginsToExport = getActivePersistentPlugins(
pluginStates,
pluginMessageQueue,
plugins,
selectedClient,
);
return {
share,
selectedPlugins: plugins.selectedPlugins,
availablePluginsToExport,
};
},
(dispatch: Dispatch<Action<any>>) => ({

View File

@@ -46,6 +46,7 @@ type Props = {
onHide: () => any;
elements: Array<Element>;
title?: string;
leftPadding?: number;
} & SubType;
const Title = styled(Text)({
@@ -94,6 +95,7 @@ type RowComponentProps = {
disabled: boolean;
toolTipMessage?: string;
type: SelectionType;
leftPadding?: number;
};
class RowComponent extends Component<RowComponentProps> {
@@ -106,6 +108,7 @@ class RowComponent extends Component<RowComponentProps> {
disabled,
toolTipMessage,
type,
leftPadding,
} = this.props;
return (
<FlexColumn>
@@ -116,7 +119,7 @@ class RowComponent extends Component<RowComponentProps> {
paddingRight={0}
paddingTop={8}
paddingBottom={8}
paddingLeft={0}>
paddingLeft={leftPadding || 0}>
<FlexRow style={{alignItems: 'center'}}>
<Text color={disabled ? colors.light20 : undefined}>{label}</Text>
<Spacer />
@@ -191,7 +194,7 @@ export default class ListView extends Component<Props, State> {
};
render() {
const {onSubmit, type} = this.props;
const {onSubmit, type, leftPadding} = this.props;
return (
<Container>
<FlexColumn>
@@ -208,6 +211,7 @@ export default class ListView extends Component<Props, State> {
onChange={this.handleChange}
disabled={unselectable != null}
toolTipMessage={unselectable?.toolTipMessage}
leftPadding={leftPadding}
/>
);
})}
@@ -217,9 +221,11 @@ export default class ListView extends Component<Props, State> {
<Padder paddingTop={8} paddingBottom={2}>
<FlexRow>
<Spacer />
<Button compact padded onClick={this.props.onHide}>
Close
</Button>
<Padder paddingRight={8}>
<Button compact padded onClick={this.props.onHide}>
Close
</Button>
</Padder>
<Tooltip
title={
this.state.selectedElements.size <= 0

View File

@@ -0,0 +1,108 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import React from 'react';
import Client from '../../Client';
import {create, act, ReactTestRenderer} from 'react-test-renderer';
import configureStore from 'redux-mock-store';
import {Provider} from 'react-redux';
import {default as BaseDevice} from '../../devices/BaseDevice';
import ExportDataPluginSheet from '../ExportDataPluginSheet';
import {FlipperPlugin, FlipperDevicePlugin} from '../../plugin';
function generateClientIdentifier(device: BaseDevice, app: string): string {
const {os, deviceType, serial} = device;
const identifier = `${app}#${os}#${deviceType}#${serial}`;
return identifier;
}
class TestPlugin extends FlipperPlugin<any, any, any> {}
TestPlugin.title = 'TestPlugin';
TestPlugin.id = 'TestPlugin';
TestPlugin.defaultPersistedState = {msg: 'Test plugin'};
class TestDevicePlugin extends FlipperDevicePlugin<any, any, any> {}
TestDevicePlugin.title = 'TestDevicePlugin';
TestDevicePlugin.id = 'TestDevicePlugin';
TestDevicePlugin.defaultPersistedState = {msg: 'TestDevicePlugin'};
function getStore(selectedPlugins: Array<string>) {
const logger = {
track: () => {},
info: () => {},
warn: () => {},
error: () => {},
debug: () => {},
trackTimeSince: () => {},
};
let mockStore = configureStore([])();
const selectedDevice = new BaseDevice(
'serial',
'emulator',
'TestiPhone',
'iOS',
);
const client = new Client(
generateClientIdentifier(selectedDevice, 'app'),
{app: 'app', os: 'iOS', device: 'TestiPhone', device_id: 'serial'},
null,
logger,
// @ts-ignore
mockStore,
['TestPlugin', 'TestDevicePlugin'],
);
const pluginStates: {[key: string]: any} = {};
pluginStates[`${client.id}#TestDevicePlugin`] = {
msg: 'Test Device plugin',
};
pluginStates[`${client.id}#TestPlugin`] = {
msg: 'Test plugin',
};
mockStore = configureStore([])({
application: {share: {closeOnFinish: false, type: 'link'}},
plugins: {
clientPlugins: new Map([['TestPlugin', TestPlugin]]),
devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]),
gatekeepedPlugins: [],
disabledPlugins: [],
failedPlugins: [],
selectedPlugins,
},
pluginStates,
pluginMessageQueue: [],
connections: {selectedApp: client.id, clients: [client]},
});
return mockStore;
}
test('SettingsSheet snapshot with nothing enabled', async () => {
let root: ReactTestRenderer;
await act(async () => {
root = create(
<Provider store={getStore([])}>
<ExportDataPluginSheet onHide={() => {}} />
</Provider>,
);
});
expect(root!.toJSON()).toMatchSnapshot();
});
test('SettingsSheet snapshot with one plugin enabled', async () => {
let root: ReactTestRenderer;
await act(async () => {
root = create(
<Provider store={getStore(['TestPlugin'])}>
<ExportDataPluginSheet onHide={() => {}} />
</Provider>,
);
});
expect(root!.toJSON()).toMatchSnapshot();
});

View File

@@ -0,0 +1,291 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SettingsSheet snapshot with nothing enabled 1`] = `
<div
className="css-1alsdwv"
>
<div
className="css-14m1iua"
>
<div
className="css-1k4677w"
>
<span
className="css-15682x"
>
Select the plugins for which you want to export the data
</span>
<div
className="css-9dmkpi"
>
<div
className="css-1k4677w"
>
<div
className="css-1obf64m"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<div
className="css-1jrm6r3"
>
<div
className="css-a1glw"
style={
Object {
"alignItems": "center",
}
}
>
<span
className="css-1cegttb"
>
TestDevicePlugin
</span>
<div
className="css-12zzrdt"
/>
<input
checked={false}
className="css-1y77uts"
disabled={false}
onChange={[Function]}
type="checkbox"
/>
</div>
</div>
<div
className="css-1nj3es"
/>
</div>
</div>
<div
className="css-1k4677w"
>
<div
className="css-1obf64m"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<div
className="css-1jrm6r3"
>
<div
className="css-a1glw"
style={
Object {
"alignItems": "center",
}
}
>
<span
className="css-1cegttb"
>
TestPlugin
</span>
<div
className="css-12zzrdt"
/>
<input
checked={false}
className="css-1y77uts"
disabled={false}
onChange={[Function]}
type="checkbox"
/>
</div>
</div>
<div
className="css-1nj3es"
/>
</div>
</div>
</div>
</div>
<div
className="css-1yqvjo0"
>
<div
className="css-a1glw"
>
<div
className="css-12zzrdt"
/>
<div
className="css-12n892b"
>
<div
className="css-1ee9nwd"
onClick={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
>
Close
</div>
</div>
<div
className="css-1obf64m"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<div
className="css-6lxjsf"
disabled={true}
onClick={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
type="primary"
>
Submit
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`SettingsSheet snapshot with one plugin enabled 1`] = `
<div
className="css-1alsdwv"
>
<div
className="css-14m1iua"
>
<div
className="css-1k4677w"
>
<span
className="css-15682x"
>
Select the plugins for which you want to export the data
</span>
<div
className="css-9dmkpi"
>
<div
className="css-1k4677w"
>
<div
className="css-1obf64m"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<div
className="css-1jrm6r3"
>
<div
className="css-a1glw"
style={
Object {
"alignItems": "center",
}
}
>
<span
className="css-1cegttb"
>
TestDevicePlugin
</span>
<div
className="css-12zzrdt"
/>
<input
checked={false}
className="css-1y77uts"
disabled={false}
onChange={[Function]}
type="checkbox"
/>
</div>
</div>
<div
className="css-1nj3es"
/>
</div>
</div>
<div
className="css-1k4677w"
>
<div
className="css-1obf64m"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<div
className="css-1jrm6r3"
>
<div
className="css-a1glw"
style={
Object {
"alignItems": "center",
}
}
>
<span
className="css-1cegttb"
>
TestPlugin
</span>
<div
className="css-12zzrdt"
/>
<input
checked={true}
className="css-1y77uts"
disabled={false}
onChange={[Function]}
type="checkbox"
/>
</div>
</div>
<div
className="css-1nj3es"
/>
</div>
</div>
</div>
</div>
<div
className="css-1yqvjo0"
>
<div
className="css-a1glw"
>
<div
className="css-12zzrdt"
/>
<div
className="css-12n892b"
>
<div
className="css-1ee9nwd"
onClick={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
>
Close
</div>
</div>
<div
className="css-1obf64m"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<div
className="css-1ee9nwd"
disabled={false}
onClick={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
type="primary"
>
Submit
</div>
</div>
</div>
</div>
</div>
</div>
`;