Introduce Tabs
Summary: Ant'd tabs didn't allow for vertical fill out. Introduced our own tiny wrapper that has `grow` by default. Also made sure the users last selection is remembered. Reviewed By: cekkaewnumchai Differential Revision: D28026345 fbshipit-source-id: 7703bc241cd1427336b7c917bdb5be9f56bba9b9
This commit is contained in:
committed by
Facebook GitHub Bot
parent
fe10e9dcc2
commit
37a7b16774
@@ -8,9 +8,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Typography, Card, Table, Collapse, Button, Tabs} from 'antd';
|
import {Typography, Card, Table, Collapse, Button} from 'antd';
|
||||||
import {Layout, Link} from '../ui';
|
import {Layout, Link} from '../ui';
|
||||||
import {NUX, Panel, theme, Tracked, TrackingScope} from 'flipper-plugin';
|
import {
|
||||||
|
NUX,
|
||||||
|
Panel,
|
||||||
|
theme,
|
||||||
|
Tracked,
|
||||||
|
TrackingScope,
|
||||||
|
Tabs,
|
||||||
|
Tab,
|
||||||
|
} from 'flipper-plugin';
|
||||||
import reactElementToJSXString from 'react-element-to-jsx-string';
|
import reactElementToJSXString from 'react-element-to-jsx-string';
|
||||||
import {CodeOutlined} from '@ant-design/icons';
|
import {CodeOutlined} from '@ant-design/icons';
|
||||||
|
|
||||||
@@ -341,6 +349,52 @@ const demos: PreviewProps[] = [
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Tabs / Tab',
|
||||||
|
description:
|
||||||
|
"Tabs represents a tab control and all it's children should be Tab components. By default the Tab control uses all available space, but set grow=false to only use the minimally required space",
|
||||||
|
props: [
|
||||||
|
[
|
||||||
|
'grow (Tabs)',
|
||||||
|
'boolean (true)',
|
||||||
|
'If true, the tab control will grow all tabs to the maximum available vertical space. If false, only the minimal required (natural) vertical space will be used',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'pad / gap (Tab)',
|
||||||
|
'boolean / number (false)',
|
||||||
|
'See the pad property of Layout.Container, determines whether the pane contents will have some padding and space between the items. By default no padding / gap is applied.',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'other props',
|
||||||
|
'',
|
||||||
|
'This component wraps Tabs from ant design, see https://ant.design/components/tabs/ for more details',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
demos: {
|
||||||
|
'Two tabs': (
|
||||||
|
<Layout.Container height={200}>
|
||||||
|
<Tabs>
|
||||||
|
<Tab tab="Pane 1">{aDynamicBox}</Tab>
|
||||||
|
<Tab tab="Pane 2 pad gap" pad gap>
|
||||||
|
{aFixedHeightBox}
|
||||||
|
{aFixedHeightBox}
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
</Layout.Container>
|
||||||
|
),
|
||||||
|
'Two tabs (no grow)': (
|
||||||
|
<Layout.Container grow={false}>
|
||||||
|
<Tabs>
|
||||||
|
<Tab tab="Pane 1">{aDynamicBox}</Tab>
|
||||||
|
<Tab tab="Pane 2 pad gap" pad gap>
|
||||||
|
{aFixedHeightBox}
|
||||||
|
{aFixedHeightBox}
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
</Layout.Container>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'NUX',
|
title: 'NUX',
|
||||||
description:
|
description:
|
||||||
@@ -420,7 +474,7 @@ function ComponentPreview({title, demos, description, props}: PreviewProps) {
|
|||||||
<Layout.Container gap="large">
|
<Layout.Container gap="large">
|
||||||
{Object.entries(demos).map(([name, children]) => (
|
{Object.entries(demos).map(([name, children]) => (
|
||||||
<Tabs type="line" key={name}>
|
<Tabs type="line" key={name}>
|
||||||
<Tabs.TabPane tab={name} key="1">
|
<Tab tab={name} key="1">
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
background: theme.backgroundWash,
|
background: theme.backgroundWash,
|
||||||
@@ -428,8 +482,8 @@ function ComponentPreview({title, demos, description, props}: PreviewProps) {
|
|||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</Tabs.TabPane>
|
</Tab>
|
||||||
<Tabs.TabPane tab={<CodeOutlined />} key="2">
|
<Tab tab={<CodeOutlined />} key="2">
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
background: theme.backgroundWash,
|
background: theme.backgroundWash,
|
||||||
@@ -438,7 +492,7 @@ function ComponentPreview({title, demos, description, props}: PreviewProps) {
|
|||||||
}}>
|
}}>
|
||||||
<pre>{reactElementToJSXString(children)}</pre>
|
<pre>{reactElementToJSXString(children)}</pre>
|
||||||
</div>
|
</div>
|
||||||
</Tabs.TabPane>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
))}
|
))}
|
||||||
</Layout.Container>
|
</Layout.Container>
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ test('Correct top level API exposed', () => {
|
|||||||
"MarkerTimeline",
|
"MarkerTimeline",
|
||||||
"NUX",
|
"NUX",
|
||||||
"Panel",
|
"Panel",
|
||||||
|
"Tab",
|
||||||
|
"Tabs",
|
||||||
"TestUtils",
|
"TestUtils",
|
||||||
"Tracked",
|
"Tracked",
|
||||||
"TrackingScope",
|
"TrackingScope",
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ export {
|
|||||||
InteractiveProps as _InteractiveProps,
|
InteractiveProps as _InteractiveProps,
|
||||||
} from './ui/Interactive';
|
} from './ui/Interactive';
|
||||||
export {Panel} from './ui/Panel';
|
export {Panel} from './ui/Panel';
|
||||||
|
export {Tabs, Tab} from './ui/Tabs';
|
||||||
export {useLocalStorageState} from './utils/useLocalStorageState';
|
export {useLocalStorageState} from './utils/useLocalStorageState';
|
||||||
|
|
||||||
export {HighlightManager} from './ui/Highlight';
|
export {HighlightManager} from './ui/Highlight';
|
||||||
|
|||||||
89
desktop/flipper-plugin/src/ui/Tabs.tsx
Normal file
89
desktop/flipper-plugin/src/ui/Tabs.tsx
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* 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 * as React from 'react';
|
||||||
|
import {Children} from 'react';
|
||||||
|
import {Tabs as AntdTabs, TabsProps, TabPaneProps} from 'antd';
|
||||||
|
import {css, cx} from '@emotion/css';
|
||||||
|
import {Layout} from './Layout';
|
||||||
|
import {Spacing} from './theme';
|
||||||
|
import {useLocalStorageState} from '../utils/useLocalStorageState';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Tabs component.
|
||||||
|
*/
|
||||||
|
export function Tabs({
|
||||||
|
grow,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...baseProps
|
||||||
|
}: {grow?: boolean} & TabsProps) {
|
||||||
|
const keys: string[] = [];
|
||||||
|
const keyedChildren = Children.map(children, (child: any, idx) => {
|
||||||
|
if (!child || typeof child !== 'object') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tabKey =
|
||||||
|
(typeof child.props.tab === 'string' && child.props.tab) || `tab_${idx}`;
|
||||||
|
keys.push(tabKey);
|
||||||
|
return {
|
||||||
|
...child,
|
||||||
|
props: {
|
||||||
|
...child.props,
|
||||||
|
key: tabKey,
|
||||||
|
tabKey,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const [activeTab, setActiveTab] = useLocalStorageState<string | undefined>(
|
||||||
|
'Tabs:' + keys.join(','),
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AntdTabs
|
||||||
|
activeKey={activeTab}
|
||||||
|
onChange={(key) => {
|
||||||
|
setActiveTab(key);
|
||||||
|
}}
|
||||||
|
{...baseProps}
|
||||||
|
className={cx(className, grow !== false ? growingTabs : undefined)}>
|
||||||
|
{keyedChildren}
|
||||||
|
</AntdTabs>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Tab: React.FC<
|
||||||
|
TabPaneProps & {
|
||||||
|
pad?: Spacing;
|
||||||
|
gap?: Spacing;
|
||||||
|
}
|
||||||
|
> = function Tab({pad, gap, children, ...baseProps}) {
|
||||||
|
return (
|
||||||
|
<AntdTabs.TabPane {...baseProps}>
|
||||||
|
<Layout.Container gap={gap} pad={pad} grow>
|
||||||
|
{children}
|
||||||
|
</Layout.Container>
|
||||||
|
</AntdTabs.TabPane>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const growingTabs = css`
|
||||||
|
flex: 1;
|
||||||
|
& .tabpanel {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
& .ant-tabs-content {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
& .ant-tabs-tabpane {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -16,7 +16,7 @@ index ac93b76..edae9be 100644
|
|||||||
+ 'Tabs',
|
+ 'Tabs',
|
||||||
+ 'onTabClick',
|
+ 'onTabClick',
|
||||||
+ scope,
|
+ scope,
|
||||||
+ 'tab:' + key + ':' + tab,
|
+ 'tab:' + key,
|
||||||
+ onClick,
|
+ onClick,
|
||||||
+ e
|
+ e
|
||||||
+ );
|
+ );
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ For conversion, the following table maps the old components to the new ones:
|
|||||||
| `ManagedDataInspector` / `DataInspector` | `DataInspector` | `flipper-plugin` ||
|
| `ManagedDataInspector` / `DataInspector` | `DataInspector` | `flipper-plugin` ||
|
||||||
| `ManagedElementInspector` / `ElementInspector` | `ElementInspector` | `flipper-plugin` ||
|
| `ManagedElementInspector` / `ElementInspector` | `ElementInspector` | `flipper-plugin` ||
|
||||||
| `Panel` | `Panel` | `flipper-plugin` ||
|
| `Panel` | `Panel` | `flipper-plugin` ||
|
||||||
| `Tabs` / `Tab` | `Tabs` / `Tab` | `flipper-plugin ||
|
| `Tabs` / `Tab` | `Tabs` / `Tab` | `flipper-plugin | Note that `Tab`'s `title` property is now called `tab`. |
|
||||||
|
|
||||||
Most other components, like `select` elements, tabs, date-pickers, etc etc can all be found in the Ant documentaiton.
|
Most other components, like `select` elements, tabs, date-pickers, etc etc can all be found in the Ant documentaiton.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user