Skip to content

Commit

Permalink
fix: refactor bock components
Browse files Browse the repository at this point in the history
  • Loading branch information
atanasster committed Mar 19, 2020
1 parent 8114ef5 commit 71ba0de
Show file tree
Hide file tree
Showing 10 changed files with 260 additions and 63 deletions.
2 changes: 1 addition & 1 deletion ui/blocks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

A collection of core block components - intended to be displayed on the documentation page of stories.

Some of the guidingdesign goals for this library:
Some of the guiding design goals for this library:

- Most components should have a 'plain' and a 'block' version, where the block versions adds a collapsible ox with a title.
- There are two main categories of components: components that display story data (i.e. story source, story render) and component(s) data (i.e. prop tables, component sources)
Expand Down
15 changes: 14 additions & 1 deletion ui/components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,20 @@

## @component-controls/components

A collection of the base UI components used by component-controls
A collection of commonly used UI components for component-controls

The libraries used include in no particular order:

- [theme-ui](https://theme-ui.com) as the theming and components foundation.
- [prism](https://prismjs.com) for source code syntax highlighting, rendered with [prism-react-renderer](https://github.com/FormidableLabs/prism-react-renderer).
- [markdown-to-jsx](https://probablyup.com/markdown-to-jsx/) to transform markdown to JSX at runtime.
- [react-table](https://github.com/tannerlinsley/react-table) to display tabular data.
- [octicons](https://octicons.github.com) for icons used in the components.
- [react-tabs](https://reactcommunity.org/react-tabs/) for tabs and multi-page components.
- [react-popper-tooltip](https://react-popper-tooltip.netlify.com) for popups and tooltips.
- [react-animate-height](https://muffinman.io/react-animate-height/) for collapsible components.
- [@theme-ui/presets](https://theme-ui.com/packages/presets/) for custom theming.


# List of components

Expand Down
50 changes: 46 additions & 4 deletions ui/components/src/ActionBar/ActionBar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ const Container: React.FC = ({ children }) => (
</Box>
</ThemeProvider>
);
export const simple = () => (
export const overview = () => (
<Container>
<ActionBar
actionItems={[
actions={[
{
title: 'action 1',
onClick: () => console.log('clicked'),
Expand All @@ -41,7 +41,7 @@ export const simple = () => (
export const disabled = () => (
<Container>
<ActionBar
actionItems={[
actions={[
{
title: 'click action',
onClick: () => console.log('clicked'),
Expand All @@ -55,11 +55,53 @@ export const disabled = () => (
export const link = () => (
<Container>
<ActionBar
actionItems={[
actions={[
{
title: <ExternalLink href="https://google.com">google</ExternalLink>,
},
]}
/>
</Container>
);

export const order = () => (
<Container>
<ActionBar
actions={[
{
title: 'action 1',
onClick: () => console.log('clicked'),
order: 1,
},
{
title: <ExternalLink href="https://google.com">google</ExternalLink>,
onClick: () => console.log('clicked'),
order: 0,
},
]}
/>
</Container>
);

export const override = () => (
<Container>
<ActionBar
actions={[
{
title: 'action 1',
onClick: () => console.log('clicked'),
id: 'copy',
},
{
title: <ExternalLink href="https://google.com">google</ExternalLink>,
onClick: () => console.log('clicked'),
},
{
//this will override the action above
title: 'Copy',
id: 'copy',
},
]}
/>
</Container>
);
93 changes: 72 additions & 21 deletions ui/components/src/ActionBar/ActionBar.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,43 @@
/** @jsx jsx */
import React, { FunctionComponent, MouseEvent } from 'react';
import styled from '@emotion/styled';
import { transparentize } from 'polished';
import { Theme, Box, Button, jsx, useThemeUI } from 'theme-ui';
import { Theme, Box, Flex, Button, jsx, useThemeUI } from 'theme-ui';

/**
* an item in the ActionBar component
*/
export interface ActionItem {
/**
* optional id, used if title is not set
*/
id?: string;
/**
* title - if a string, will use the Button component, else can prvide custom React component
*/
title: React.ReactNode;
/**
* onClick event when passing a string as the title
*/
onClick?: (e: MouseEvent<HTMLButtonElement>) => void;
/**
* displays the Button as disabled
*/
disabled?: boolean;
/**
* hide an action item
*/
hidden?: boolean;

/**
* optional order, if not provided will use the natural order of items from right to left
*/
order?: number;
}

export interface ActionBarProps {
actionItems: ActionItem[];
actions: ActionItem[];
}

const StyledContainer = styled.div`
position: relative;
`;

const StyledFlex = styled.div`
display: flex;
position: absolute;
flex-direction: row-reverse;
width: 100%;
`;

const ActionColors = ({
theme,
disabled,
Expand All @@ -51,16 +63,55 @@ const ActionColors = ({
border: `1px solid ${theme.colors?.['highlight'] as string}`,
});

/**
* a strip of actions to be attached to a container
* the action items contain the labels and click event handler
* actions can accept an order prop, and can also be superimposed
*
*/
export const ActionBar: FunctionComponent<ActionBarProps> = ({
actionItems,
actions = [],
}) => {
const { theme } = useThemeUI();
return (
<StyledContainer>
<StyledFlex>
{actionItems
<Box
sx={{
position: 'relative',
}}
>
<Flex
sx={{
position: 'absolute',
flexDirection: 'row-reverse',
width: '100%',
}}
>
{actions
.filter(({ hidden }) => !hidden)
.map(({ title, onClick, disabled }, index: number) => (
.reduce((acc: ActionItem[], item: ActionItem) => {
const accIndex = acc.findIndex(
accItem =>
(accItem.id ?? accItem.title) === (item.id ?? item.title),
);
if (accIndex > -1) {
acc[accIndex] = { ...acc[accIndex], ...item };
return acc;
} else {
return [...acc, item];
}
}, [])
.map(
({ order, ...item }, index) =>
({
...item,
order: order ?? index,
} as ActionItem),
)
.sort((a: ActionItem, b: ActionItem) => {
//@ts-ignore
return a.order - b.order;
})
.map(({ title, onClick, disabled }, index) => (
<Box
key={`${typeof title === 'string' ? title : 'item'}_${index}`}
sx={{
Expand All @@ -80,7 +131,7 @@ export const ActionBar: FunctionComponent<ActionBarProps> = ({
)}
</Box>
))}
</StyledFlex>
</StyledContainer>
</Flex>
</Box>
);
};
70 changes: 70 additions & 0 deletions ui/components/src/ActionContainer/ActionContainer.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';
import { Donut } from 'theme-ui';
import { ExternalLink } from '../ExternalLink';
import { ActionContainer } from './ActionContainer';

export default {
title: 'Components/ActionContainer',
component: ActionContainer,
};

export const overview = () => {
return (
<ActionContainer
actions={[
{
title: 'action 1',
onClick: () => console.log('clicked'),
},
{
title: <ExternalLink href="https://google.com">google</ExternalLink>,
onClick: () => console.log('clicked'),
},
]}
>
<Donut value={1 / 2} />
</ActionContainer>
);
};

export const order = () => (
<ActionContainer
actions={[
{
title: 'action 1',
onClick: () => console.log('clicked'),
order: 1,
},
{
title: <ExternalLink href="https://google.com">google</ExternalLink>,
onClick: () => console.log('clicked'),
order: 0,
},
]}
>
<Donut value={1 / 2} />
</ActionContainer>
);

export const override = () => (
<ActionContainer
actions={[
{
title: 'action 1',
onClick: () => console.log('clicked'),
id: 'copy',
},
{
title: <ExternalLink href="https://google.com">google</ExternalLink>,
onClick: () => console.log('clicked'),
},
{
//this will override the action above
title: 'Copy',
id: 'copy',
},
]}
>
<Donut value={1 / 2} />
</ActionContainer>
);
44 changes: 44 additions & 0 deletions ui/components/src/ActionContainer/ActionContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/** @jsx jsx */
import { FC } from 'react';
import { transparentize } from 'polished';
import { jsx, Box, useThemeUI } from 'theme-ui';
import { ActionBar, ActionItem } from '../ActionBar';

export interface ActionContainerProps {
/**
* optional actions provided to the component
*/
actions?: ActionItem[];
}

/**
* a boxed container with actions.
*/
export const ActionContainer: FC<ActionContainerProps> = ({
children,
actions,
}) => {
const { theme } = useThemeUI();
return (
<div>
{actions && <ActionBar actions={actions} />}
<Box
sx={{
borderRadius: 4,
boxShadow: `${transparentize(
0.9,
theme.colors?.text as string,
)} 0 1px 3px 1px, ${transparentize(
0.9,
theme.colors?.text as string,
)} 0 0 0 1px`,
'*:first-child': {
paddingTop: 3,
},
}}
>
{children}
</Box>
</div>
);
};
1 change: 1 addition & 0 deletions ui/components/src/ActionContainer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ActionContainer';
9 changes: 3 additions & 6 deletions ui/components/src/BlockContainer/BlockContainer.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,15 @@ export default {
component: BlockContainer,
};

export const simple = ({ title }: BlockContainerProps) => {
export const overview = ({ title }: BlockContainerProps) => {
return (
<BlockContainer
title={title}
actions={[{ title: 'click me', onClick: () => console.log('clicked') }]}
>
<BlockContainer title={title}>
<Donut value={1 / 2} />
</BlockContainer>
);
};

simple.story = {
overview.story = {
controls: {
title: { type: 'text', value: 'Block title' },
},
Expand Down
Loading

0 comments on commit 71ba0de

Please sign in to comment.