Skip to content

Commit

Permalink
feat: navigation (#91)
Browse files Browse the repository at this point in the history
  • Loading branch information
drinchev authored Nov 27, 2019
1 parent 1c7b87c commit b695131
Show file tree
Hide file tree
Showing 54 changed files with 2,032 additions and 19 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,19 @@ design system. It is a React.js implementation of
## Usage

Make sure to include the bundled CSS in your React Application as well as
wrapping your content in [`<Frame />`](https://lightelligence-io.github.io/react/#/Components/Frame)
wrapping your content in [`<RootContainer />`](https://lightelligence-io.github.io/react/#/Layout/RootContainer)
component.

```jsx
import React from 'react';
import ReactDOM from 'react-dom';
import '@lightelligence/react/dist/index.css';
import { Button, Frame, COLOR_PRIMARY } from '@lightelligence/react';
import { Button, RootContainer, COLOR_PRIMARY } from '@lightelligence/react';

const App = () => (
<Frame>
<RootContainer>
<Button color={COLOR_PRIMARY}>Hello World</Button>
</Frame>
</RootContainer>
);

ReactDOM.render(<App />, document.getElementById('root'));
Expand Down
6 changes: 6 additions & 0 deletions resources/lightelligence.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 7 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,15 @@ export * from './controls/TextArea';
export * from './controls/Input';

export * from './layout/Container';
export * from './layout/Frame';
export * from './layout/Grid';
export * from './layout/Theme';
export * from './layout/RootContainer';

export * from './navigation/Header';
export * from './navigation/Sidebar';
export * from './navigation/SidebarNavigation';
export * from './navigation/SidebarSelector';
export * from './navigation/SecondarySidebar';

export * from './constants';

Expand Down
8 changes: 0 additions & 8 deletions src/layout/Frame/Frame.md

This file was deleted.

1 change: 0 additions & 1 deletion src/layout/Frame/index.js

This file was deleted.

38 changes: 38 additions & 0 deletions src/layout/RootContainer/RootContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { string, node } from 'prop-types';
import React from 'react';
import classnames from 'classnames';
import * as olt from '@lightelligence/styles';

/**
* Use the RootContainer component to build a layout of your application.
*
* It gives you a wrapper that will properly position :
*
* - [Header Component](#/Navigation/Header)
* - [Sidebar Component](#/Navigation/Sidebar)
* - [SecondarySidebar Component](#/Navigation/SecondarySidebar)
* - [RootMainContainer Component](#/Layout/RootMainContainer)
*/
export const RootContainer = ({ className, children, ...props }) => (
<div {...props} className={classnames(olt.Frame, olt.Layout, className)}>
{children}
</div>
);

RootContainer.propTypes = {
/**
* Forward an additional className to the underlying element
*/
className: string,
/**
* The body of the layout. To work correctly you must include
* [Header](#/Navigation/Header), [Sidebar](#/Navigation/Sidebar) and
* [RootMainContainer](#/Layout/RootMainContainer)
*/
children: node,
};

RootContainer.defaultProps = {
className: null,
children: null,
};
121 changes: 121 additions & 0 deletions src/layout/RootContainer/RootContainer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
### Example

Note that in this example we take care of triggering the
[SecondarySidebar](#/Navigation/SecondarySidebar) with a simple use of
`React.useState`. The user has total control of the interaction between the
[Header](#/Navigation/Header), [Sidebar](#/Navigation/Sidebar) and
[SecondarySidebar](#/Navigation/SecondarySidebar)

```js
import { MemoryRouter } from 'react-router';
import {
RootContainer,
Header,
ActionButton,
Tabs,
Tab,
Sidebar,
SidebarSeparator,
SidebarSelectorProperty,
SidebarSelectorTenant,
SidebarSelectorFilter,
SidebarSelectorFilterItem,
SidebarNavigation,
SidebarNavigationItem,
SidebarSubNavigationItem,
RootMainContainer,
SecondarySidebar,
Card,
} from '@lightelligence/react';

const logo = require('../../../resources/lightelligence.svg');

const [currentNavigation, setCurrentNavigation] = React.useState(false);

const MyHeader = () => (
<Header
style={{ position: 'static' }} /* Used for demo purposes */
logo={<img src={logo} />}
left={
<ActionButton label="Back" iconLeft="chevron-left" buttonType="default" />
}
right={
<ActionButton label="Logout" buttonType="default" iconRight="locked" />
}
>
<Tabs value="details">
<Tab label="Details" value="details" />
<Tab label="Permissions" />
<Tab label="Redirects" />
<Tab label="Tenants" />
</Tabs>
</Header>
);

const MySidebar = () => (
<Sidebar
style={{ position: 'absolute', minHeight: '600px' }}
bottom={[
<SidebarSelectorProperty
title="Property Name"
location="Berlin, Germany"
onClick={() =>
setCurrentNavigation(
currentNavigation === 'property' ? null : 'property',
)
}
/>,
<SidebarSelectorTenant
tenant="Tenant Name"
onClick={() => alert('tenant selector')}
/>,
]}
>
<SidebarSelectorFilter
onClick={() =>
setCurrentNavigation(currentNavigation === 'filter' ? null : 'filter')
}
>
<SidebarSelectorFilterItem icon="office" name="Forschungszentrum" />
<SidebarSelectorFilterItem icon="floorplan" name="2nd Floor" />
</SidebarSelectorFilter>
<SidebarSeparator />
<MemoryRouter>
<SidebarNavigation>
<SidebarNavigationItem icon="home" title="Home" to="/home" />
<SidebarNavigationItem icon="sensor" title="Devices" to="/devices">
<SidebarSubNavigationItem title="Devices" to="/devices/devices" />
<SidebarSubNavigationItem title="Types" to="/devices/types" />
</SidebarNavigationItem>
<SidebarNavigationItem
icon="app"
title="Applications"
to="/applications"
/>
<SidebarNavigationItem icon="code" title="Code" to="/developer" />
<SidebarNavigationItem icon="user-default" title="Team" to="/team" />
</SidebarNavigation>
</MemoryRouter>
</Sidebar>
);

<div style={{ position: 'relative', minHeight: '600px', overflow: 'hidden' }}>
<RootContainer>
<MyHeader />
<MySidebar />
<SecondarySidebar
style={{ position: 'absolute' }}
header="Filters"
open={currentNavigation === 'filter'}
/>
<SecondarySidebar
style={{ position: 'absolute' }}
header="Properties"
open={currentNavigation === 'property'}
/>
<RootMainContainer>
<Card>Content</Card>
</RootMainContainer>
</RootContainer>
</div>;
```
34 changes: 34 additions & 0 deletions src/layout/RootContainer/RootContainer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { render } from '@testing-library/react';
import { RootContainer } from './RootContainer';
import { oltStyles } from '../..';

const renderComponent = (props) => {
return render(<RootContainer {...props} data-testid="root-container" />);
};

describe('RootContainer', () => {
test('has oltStyles.Layout and oltStyles.Frame', () => {
const { getByTestId } = renderComponent();

const component = getByTestId('root-container');
expect(component.classList.contains(oltStyles.Layout)).toBe(true);
expect(component.classList.contains(oltStyles.Frame)).toBe(true);
});
test('forwards className', () => {
const { getByTestId } = renderComponent({
className: 'myClass',
});

const component = getByTestId('root-container');
expect(component.classList.contains('myClass')).toBe(true);
});
test('renders children', () => {
const { getByText } = renderComponent({
children: 'Foo',
});

const component = getByText('Foo');
expect(component).toBeTruthy();
});
});
44 changes: 44 additions & 0 deletions src/layout/RootContainer/RootMainContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { node, string } from 'prop-types';
import React from 'react';
import classnames from 'classnames';
import * as olt from '@lightelligence/styles';

/**
* The RootMainContainer is used in the
* [RootContainer Component](#/Layout/RootContainer) as the container of your
* body.
*
* It includes an Overlay, which is used to "blur" the content whenever
* a [SecondarySidebar](#/Navigation/SecondarySidebar) is active.
*
* The RootMainContainer passes all props to the container of the content,
* which is using the semantic `main` HTML element.
*
* The RootMainContainer also has predefined padding, according to the
* RootContainer's [Header](#/Navigation/Header) and
* [Sidebar](#/Navigation/Sidebar).
*/
export const RootMainContainer = ({ className, children, ...props }) => (
<>
<div className={classnames(olt.LayoutOverlay)} />
<main {...props} className={classnames(olt.LayoutBody, className)}>
{children}
</main>
</>
);

RootMainContainer.propTypes = {
/**
* Forward an additional className to the underlying element
*/
className: string,
/**
* Body of the layout is the main content of your application
*/
children: node,
};

RootMainContainer.defaultProps = {
className: null,
children: null,
};
Loading

0 comments on commit b695131

Please sign in to comment.