Introduce NUX element
Summary:
allow-large-files
This diff introces the `NUX` element that can be wrapped around any other element to give a first-time usage hint.
Hint dismissal is stored by taking a hash of the hint contents, and scoped per plugin.
Users can reset the 'read' status in the settings page
Example usage:
```
<NUX
title="Use bookmarks to directly navigate to a location in the app."
placement="right">
<Input addonAfter={<SettingOutlined />} defaultValue="mysite" />
</NUX>
```
Reviewed By: nikoant
Differential Revision: D24622276
fbshipit-source-id: 0265634f9ab50c32214b74f033f59482cd986f23
This commit is contained in:
committed by
Facebook GitHub Bot
parent
b8b9c4296a
commit
2b0e93a063
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import {FlexColumn, Button, styled, Text, FlexRow, Spacer} from '../ui';
|
||||
import React, {Component} from 'react';
|
||||
import React, {Component, useContext} from 'react';
|
||||
import {updateSettings, Action} from '../reducers/settings';
|
||||
import {
|
||||
Action as LauncherAction,
|
||||
@@ -28,6 +28,7 @@ import LauncherSettingsPanel from '../fb-stubs/LauncherSettingsPanel';
|
||||
import SandySettingsPanel from '../fb-stubs/SandySettingsPanel';
|
||||
import {reportUsage} from '../utils/metrics';
|
||||
import {Modal} from 'antd';
|
||||
import {Layout, NuxManagerContext} from 'flipper-plugin';
|
||||
|
||||
const Container = styled(FlexColumn)({
|
||||
padding: 20,
|
||||
@@ -311,6 +312,10 @@ class SettingsSheet extends Component<Props, State> {
|
||||
}}
|
||||
/>
|
||||
</ToggledSection>
|
||||
<Layout.Right center>
|
||||
<span>Reset all new user tooltips</span>
|
||||
<ResetTooltips />
|
||||
</Layout.Right>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -351,3 +356,16 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
||||
}),
|
||||
{updateSettings, updateLauncherSettings},
|
||||
)(SettingsSheet);
|
||||
|
||||
function ResetTooltips() {
|
||||
const nuxManager = useContext(NuxManagerContext);
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
nuxManager.resetHints();
|
||||
}}>
|
||||
Reset
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import {PopoverProvider} from './ui/components/PopoverProvider';
|
||||
import {initializeFlipperLibImplementation} from './utils/flipperLibImplementation';
|
||||
import {enableConsoleHook} from './chrome/ConsoleLogs';
|
||||
import {sideEffect} from './utils/sideEffect';
|
||||
import {NuxManagerContext, createNuxManager} from 'flipper-plugin';
|
||||
|
||||
if (process.env.NODE_ENV === 'development' && os.platform() === 'darwin') {
|
||||
// By default Node.JS has its internal certificate storage and doesn't use
|
||||
@@ -56,7 +57,9 @@ const AppFrame = () => (
|
||||
<ContextMenuProvider>
|
||||
<Provider store={store}>
|
||||
<CacheProvider value={cache}>
|
||||
<App logger={logger} />
|
||||
<NuxManagerContext.Provider value={createNuxManager()}>
|
||||
<App logger={logger} />
|
||||
</NuxManagerContext.Provider>
|
||||
</CacheProvider>
|
||||
</Provider>
|
||||
</ContextMenuProvider>
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
import React from 'react';
|
||||
import {Typography, Card, Table, Collapse, Button, Tabs} from 'antd';
|
||||
import {Layout} from '../ui';
|
||||
import {theme} from 'flipper-plugin';
|
||||
import {Layout, Link} from '../ui';
|
||||
import {NUX, theme} from 'flipper-plugin';
|
||||
import reactElementToJSXString from 'react-element-to-jsx-string';
|
||||
import {CodeOutlined} from '@ant-design/icons';
|
||||
|
||||
@@ -29,8 +29,8 @@ const demoStyle: Record<string, React.CSSProperties> = {
|
||||
|
||||
type PreviewProps = {
|
||||
title: string;
|
||||
description?: string;
|
||||
props: [string, string, string][];
|
||||
description?: React.ReactNode;
|
||||
props: [string, React.ReactNode, React.ReactNode][];
|
||||
demos: Record<string, React.ReactNode>;
|
||||
};
|
||||
|
||||
@@ -285,6 +285,31 @@ const demos: PreviewProps[] = [
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'NUX',
|
||||
description:
|
||||
'A component to provide a New-User-eXperience: Highlight new features to first time users. For tooltips that should stay available, use ToolTip from ANT design',
|
||||
props: [
|
||||
['title', 'string / React element', 'The tooltip contents'],
|
||||
[
|
||||
'placement',
|
||||
<>
|
||||
See{' '}
|
||||
<Link href="https://ant.design/components/tooltip/#components-tooltip-demo-placement">
|
||||
docs
|
||||
</Link>
|
||||
</>,
|
||||
'(optional) on which side to place the tooltip',
|
||||
],
|
||||
],
|
||||
demos: {
|
||||
'NUX example': (
|
||||
<NUX title="This button does something cool" placement="right">
|
||||
<Button>Hello world</Button>
|
||||
</NUX>
|
||||
),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
function ComponentPreview({title, demos, description, props}: PreviewProps) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import {Alert, Input} from 'antd';
|
||||
import {LeftSidebar, SidebarTitle, InfoIcon} from '../LeftSidebar';
|
||||
import {SettingOutlined} from '@ant-design/icons';
|
||||
import {Layout, Link, styled} from '../../ui';
|
||||
import {theme} from 'flipper-plugin';
|
||||
import {NUX, theme} from 'flipper-plugin';
|
||||
import {AppSelector} from './AppSelector';
|
||||
import {useStore} from '../../utils/useStore';
|
||||
import {PluginList} from './PluginList';
|
||||
@@ -49,7 +49,11 @@ export function AppInspect() {
|
||||
type="info"
|
||||
/>
|
||||
) : (
|
||||
<Input addonAfter={<SettingOutlined />} defaultValue="mysite" />
|
||||
<NUX
|
||||
title="Use bookmarks to directly navigate to a location in the app."
|
||||
placement="right">
|
||||
<Input addonAfter={<SettingOutlined />} defaultValue="mysite" />
|
||||
</NUX>
|
||||
)}
|
||||
{!isArchived && (
|
||||
<Toolbar gap>
|
||||
|
||||
@@ -12,7 +12,7 @@ import {Badge, Button, Menu, Tooltip, Typography} from 'antd';
|
||||
import {InfoIcon, SidebarTitle} from '../LeftSidebar';
|
||||
import {PlusOutlined, MinusOutlined} from '@ant-design/icons';
|
||||
import {Glyph, Layout, styled} from '../../ui';
|
||||
import {theme} from 'flipper-plugin';
|
||||
import {theme, NUX} from 'flipper-plugin';
|
||||
import {useDispatch, useStore} from '../../utils/useStore';
|
||||
import {getPluginTitle, sortPluginsByName} from '../../utils/pluginUtils';
|
||||
import {ClientPluginDefinition, DevicePluginDefinition} from '../../plugin';
|
||||
@@ -124,7 +124,10 @@ export const PluginList = memo(function PluginList() {
|
||||
</PluginGroup>
|
||||
|
||||
{!isArchived && (
|
||||
<PluginGroup key="metro" title="React Native">
|
||||
<PluginGroup
|
||||
key="metro"
|
||||
title="React Native"
|
||||
hint="The following plugins are exposed by the currently running Metro instance. Note that Metro might currently be connected to a different application or device than selected above.">
|
||||
{metroPlugins.map((plugin) => (
|
||||
<PluginEntry
|
||||
key={'metro' + plugin.id}
|
||||
@@ -163,7 +166,10 @@ export const PluginList = memo(function PluginList() {
|
||||
))}
|
||||
</PluginGroup>
|
||||
{!isArchived && (
|
||||
<PluginGroup key="disabled" title="Disabled">
|
||||
<PluginGroup
|
||||
key="disabled"
|
||||
title="Disabled"
|
||||
hint="This section shows the plugins that are currently disabled. If a pluign is enabled, you will be able to interact with it. If a plugin is disabled it won't consume resources in Flipper or in the connected application.">
|
||||
{disabledPlugins.map((plugin) => (
|
||||
<PluginEntry
|
||||
key={plugin.id}
|
||||
@@ -184,7 +190,10 @@ export const PluginList = memo(function PluginList() {
|
||||
</PluginGroup>
|
||||
)}
|
||||
{!isArchived && (
|
||||
<PluginGroup key="unavailable" title="Unavailable plugins">
|
||||
<PluginGroup
|
||||
key="unavailable"
|
||||
title="Unavailable plugins"
|
||||
hint="The plugins below are installed in Flipper, but not available for the selected device / application. Hover the plugin info box to find out why.">
|
||||
{unavailablePlugins.map(([plugin, reason]) => (
|
||||
<PluginEntry
|
||||
key={plugin.id}
|
||||
@@ -295,23 +304,40 @@ const PluginEntry = memo(function PluginEntry({
|
||||
const PluginGroup = memo(function PluginGroup({
|
||||
title,
|
||||
children,
|
||||
hint,
|
||||
...rest
|
||||
}: {title: string; children: React.ReactElement[]} & Record<string, any>) {
|
||||
}: {title: string; children: React.ReactElement[]; hint?: string} & Record<
|
||||
string,
|
||||
any
|
||||
>) {
|
||||
if (children.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let badge = (
|
||||
<Badge
|
||||
count={children.length}
|
||||
style={{
|
||||
marginRight: 20,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
if (hint) {
|
||||
badge = (
|
||||
<NUX title={hint} placement="right">
|
||||
{badge}
|
||||
</NUX>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SubMenu
|
||||
{...rest}
|
||||
title={
|
||||
<Layout.Right center>
|
||||
<Text strong>{title}</Text>
|
||||
<Badge
|
||||
count={children.length}
|
||||
style={{
|
||||
marginRight: 20,
|
||||
}}
|
||||
/>
|
||||
|
||||
{badge}
|
||||
</Layout.Right>
|
||||
}>
|
||||
{children}
|
||||
|
||||
Reference in New Issue
Block a user