Skip to content

Commit

Permalink
EUI Tree View (#2409)
Browse files Browse the repository at this point in the history
* Recursive tree component.

* Adding an expandByDefault prop.

* Swapping a div for a span

* Minor accessibility tweaks.

* wip a11y example

* Fix typescript types around aria labels

* Re-use recursive tree's context to determine nesting

* Adding i18n to the list instructions.

* Renaming to EuiTreeView.

* Add EuiI18n to aria-label text

* Adding changelog entry.

* Closer to eui spec + allows room to expand later
  • Loading branch information
daveyholler authored Oct 25, 2019
1 parent c7cb88b commit e6d693e
Show file tree
Hide file tree
Showing 19 changed files with 939 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## [`master`](https://github.com/elastic/eui/tree/master)

- Added new `euiTreeView` component for rendering recursive objects such as folder structures. ([#2409](https://github.com/elastic/eui/pull/2409))
- Added `euiXScrollWithShadows()` mixin and `.eui-xScrollWithShadows` utility class ([#2458](https://github.com/elastic/eui/pull/2458))

**Bug fixes**
Expand Down
3 changes: 3 additions & 0 deletions src-docs/src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ import { ProgressExample } from './views/progress/progress_example';

import { RangeControlExample } from './views/range/range_example';

import { TreeViewExample } from './views/tree_view/tree_view_example';

import { ResizeObserverExample } from './views/resize_observer/resize_observer_example';

import { ResponsiveExample } from './views/responsive/responsive_example';
Expand Down Expand Up @@ -310,6 +312,7 @@ const navigation = [
KeyPadMenuExample,
LinkExample,
PaginationExample,
TreeViewExample,
SideNavExample,
StepsExample,
TabsExample,
Expand Down
96 changes: 96 additions & 0 deletions src-docs/src/views/tree_view/compressed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React from 'react';

import { EuiTreeView, EuiToken } from '../../../../src/components';

export class TreeViewCompressed extends React.Component {
showAlert = () => {
alert('You squashed a bug!');
};

render() {
const items = [
{
label: 'transporter',
id: 'transporter',
icon: <EuiToken size="xs" iconType="tokenObject" />,
children: [
{
label: 'service',
id: 'service',
icon: <EuiToken size="xs" iconType="tokenString" />,
},
{
label: 'auth',
id: 'auth',
icon: <EuiToken size="xs" iconType="tokenObject" />,
children: [
{
label: 'user',
id: 'user',
icon: <EuiToken size="xs" iconType="tokenVariable" />,
},
{
label: 'pass',
id: 'pass',
icon: <EuiToken size="xs" iconType="tokenVariable" />,
},
],
},
],
},
{
label: 'getContact',
id: 'getContact',
icon: <EuiToken size="xs" iconType="tokenFunction" />,
children: [
{
label: 'render',
id: 'render',
icon: <EuiToken size="xs" iconType="tokenFunction" />,
children: [
{
label: 'title',
id: 'title',
icon: <EuiToken size="xs" iconType="tokenString" />,
},
],
},
],
},
{
label: 'postContact',
id: 'postContact',
icon: <EuiToken size="xs" iconType="tokenFunction" />,
children: [
{
label: 'errors',
id: 'errors',
icon: <EuiToken size="xs" iconType="tokenConstant" />,
},
{
label: 'mailOptions',
id: 'mailOptions',
icon: <EuiToken size="xs" iconType="tokenObject" />,
},
],
},
{
label: 'smokeMonster',
id: 'smokeMonster',
icon: <EuiToken size="xs" iconType="tokenMethod" />,
},
];

return (
<div style={{ width: '20rem' }}>
<EuiTreeView
items={items}
display="compressed"
expandByDefault
showExpansionArrows
aria-label="Document Outline"
/>
</div>
);
}
}
76 changes: 76 additions & 0 deletions src-docs/src/views/tree_view/tree_view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React from 'react';

import { EuiIcon, EuiTreeView, EuiToken } from '../../../../src/components';

export class TreeView extends React.Component {
showAlert = () => {
alert('You squashed a bug!');
};

render() {
const items = [
{
label: 'Item One',
id: 'item_one',
icon: <EuiIcon type="folderClosed" />,
iconWhenExpanded: <EuiIcon type="folderOpen" />,
isExpanded: true,
children: [
{
label: 'Item A',
id: 'item_a',
icon: <EuiIcon type="document" />,
},
{
label: 'Item B',
id: 'item_b',
icon: <EuiIcon type="arrowRight" />,
iconWhenExpanded: <EuiIcon type="arrowDown" />,
children: [
{
label: 'A Cloud',
id: 'item_cloud',
icon: <EuiToken iconType="tokenConstant" />,
},
{
label: "I'm a Bug",
id: 'item_bug',
icon: <EuiToken iconType="tokenEnum" />,
callback: this.showAlert,
},
],
},
{
label: 'Item C',
id: 'item_c',
icon: <EuiIcon type="arrowRight" />,
iconWhenExpanded: <EuiIcon type="arrowDown" />,
children: [
{
label: 'Another Cloud',
id: 'item_cloud2',
icon: <EuiToken iconType="tokenConstant" />,
},
{
label: 'Another Bug',
id: 'item_bug2',
icon: <EuiToken iconType="tokenEnum" />,
callback: this.showAlert,
},
],
},
],
},
{
label: 'Item Two',
id: 'item_two',
},
];

return (
<div style={{ width: '20rem' }}>
<EuiTreeView items={items} aria-label="Sample Folder Tree" />
</div>
);
}
}
97 changes: 97 additions & 0 deletions src-docs/src/views/tree_view/tree_view_example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React from 'react';

import { renderToHtml } from '../../services';

import { GuideSectionTypes } from '../../components';

import { EuiCode, EuiTreeView } from '../../../../src/components';
import { EuiTreeViewNode } from './tree_view_props';
import { TreeView } from './tree_view';
import { TreeViewCompressed } from './compressed';

const treeViewSource = require('!!raw-loader!./tree_view');
const treeViewHtml = renderToHtml(TreeView);

const treeViewCompressedSource = require('!!raw-loader!./compressed');
const treeViewCompressedHtml = renderToHtml(TreeViewCompressed);

export const TreeViewExample = {
title: 'Tree View',
sections: [
{
source: [
{
type: GuideSectionTypes.JS,
code: treeViewSource,
},
{
type: GuideSectionTypes.HTML,
code: treeViewHtml,
},
],
text: (
<div>
<p>
<EuiCode>EuiTreeView</EuiCode> allows you to render recursive
objects, such as a file directory. The <EuiCode>chilldren</EuiCode>{' '}
prop takes an array of <EuiCode>nodes</EuiCode>.
</p>
<p>
Keyboard navigation allows users to navigate and interact with the
tree using the arrow keys, spacebar, and return.
</p>
<p>
The <EuiCode>icon</EuiCode> prop accepts <EuiCode>EuiIcon</EuiCode>{' '}
and <EuiCode>EuiToken</EuiCode> as react nodes. You can also
specifiy a different icon for the open state with the{' '}
<EuiCode>iconWhenExpanded</EuiCode> prop.
</p>
</div>
),
components: { EuiTreeView },
demo: <TreeView />,
props: { EuiTreeView, EuiTreeViewNode },
},
{
title: 'Optional styling',
source: [
{
type: GuideSectionTypes.JS,
code: treeViewCompressedSource,
},
{
type: GuideSectionTypes.HTML,
code: treeViewCompressedHtml,
},
],
text: (
<div>
<p>
<EuiCode>EuiTreeView</EuiCode> supports a compressed mode with the{' '}
<EuiCode>display=&quot;compressed&quot;</EuiCode> setting. When
using the compressed version it&apos;s highly recommended to use the
small size of <EuiCode>EuiIcon</EuiCode> and the extra small size of{' '}
<EuiCode>EuiToken</EuiCode>. This will help prevent awkard alignment
issues when used alongside the{' '}
<EuiCode>showExpansionArrows</EuiCode> prop.
</p>
<p>
The <EuiCode>showExpansionArrows</EuiCode> prop provides an
additional visual indicator. Ideal for when a tree&apos;s items use
icons that don&apos;t immediately let a user know that there are
nested nodes that may not be visible.
</p>
<p>
In some cases, you may want to automatically expand all the items
with children. In those instances, you can use the{' '}
<EuiCode>expandByDefault</EuiCode> prop, as seen in the example
below.
</p>
</div>
),
components: { EuiTreeView },
demo: <TreeViewCompressed />,
props: { EuiTreeView, EuiTreeViewNode },
},
],
};
4 changes: 4 additions & 0 deletions src-docs/src/views/tree_view/tree_view_props.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import React, { FunctionComponent } from 'react';
import { Node } from '../../../../src/components/tree_view/tree_view';

export const EuiTreeViewNode: FunctionComponent<Node> = () => <div />;
2 changes: 1 addition & 1 deletion src/components/button/button_empty/button_empty.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ interface CommonEuiButtonEmptyProps extends CommonProps {
isLoading?: boolean;

type?: 'button' | 'submit';
buttonRef?: () => void;
buttonRef?: (ref: HTMLButtonElement | HTMLAnchorElement | null) => void;
/**
* Passes props to `euiButtonEmpty__content` span
*/
Expand Down
2 changes: 2 additions & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ export { EuiPortal } from './portal';

export { EuiProgress } from './progress';

export { EuiTreeView } from './tree_view';

export { EuiResizeObserver } from './observer/resize_observer';

export { EuiSearchBar, Query, Ast } from './search_bar';
Expand Down
1 change: 1 addition & 0 deletions src/components/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
@import 'popover/index';
@import 'portal/index';
@import 'progress/index';
@import 'tree_view/index';
@import 'side_nav/index';
@import 'spacer/index';
@import 'search_bar/index';
Expand Down
15 changes: 15 additions & 0 deletions src/components/token/__snapshots__/token.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,18 @@ exports[`EuiToken props size s is rendered 1`] = `
/>
</div>
`;

exports[`EuiToken props size xs is rendered 1`] = `
<div
class="euiToken euiToken--tokenTint01 euiToken--circle euiToken--xsmall"
>
<svg
class="euiIcon euiIcon--medium euiIcon-isLoading"
focusable="false"
height="16"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
/>
</div>
`;
9 changes: 9 additions & 0 deletions src/components/token/_token.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@
border-radius: $euiBorderRadius - 1px;
}

.euiToken--xsmall {
width: $euiSizeM;
height: $euiSizeM;

&.euiToken--rectangle {
padding: 0 $euiSizeXS;
}
}

.euiToken--small {
width: $euiSize;
height: $euiSize;
Expand Down
1 change: 1 addition & 0 deletions src/components/token/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export {
EuiToken,
EuiTokenProps,
SIZES as TOKEN_SIZES,
SHAPES as TOKEN_SHAPES,
COLORS as TOKEN_COLORS,
Expand Down
Loading

0 comments on commit e6d693e

Please sign in to comment.