Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

**Feature:** Add Contact component to for user metadata #850

Merged
merged 7 commits into from
Dec 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/Contact/Contact.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as React from "react"

import styled from "../utils/styled"

export interface ContactProps {
name: string
meta?: string
}

const Container = styled("div")`
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
overflow: auto hidden;
`

const Heading = styled("h6")<{ hasMeta: boolean }>`
margin: 0 0 ${({ theme, hasMeta }) => (hasMeta ? theme.space.base : 0)}px 0;
font-size: ${({ theme }) => theme.font.size.small}px;
font-weight: ${({ theme }) => theme.font.weight.medium};
color: ${({ theme }) => theme.color.text.dark};
line-height: 1;
`

const Meta = styled("p")`
margin: 0;
font-size: ${({ theme }) => theme.font.size.fineprint}px;
color: ${({ theme }) => theme.color.text.lightest};
line-height: 1;
`

const Contact: React.SFC<ContactProps> = ({ name, meta, ...props }) => (
<Container {...props}>
<Heading hasMeta={Boolean(meta)}>{name}</Heading>
<Meta>{meta}</Meta>
</Container>
)

export default Contact
25 changes: 25 additions & 0 deletions src/Contact/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Sometimes, we'd like to show someone's name and email address. This component lets us show a contact in a consistent way.

## Basic Usage

```jsx
<>
<Contact name="Luke Cage" meta="harlems.hero@gmail.com" />
<br />
<Contact name="Danny Rand" />
<br />
<Contact name="Matt Murdock" meta="+1 173 712 9124" />
</>
```

## With Avatar

In some cases, this component will be required to pair with an `Avatar`. Consider,

```jsx
initialState = { name: "Kenye Wheelest", email: "kweezy@notformidablelabs.com" }
;<div style={{ display: "flex" }}>
<Avatar style={{ marginRight: 8 }} name={state.name} />
<Contact name={state.name} meta={state.email} />
</div>
```
40 changes: 27 additions & 13 deletions src/ContextMenu/ContextMenu.Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ export interface Props {
}

export interface IContextMenuItem<TValue = any> {
label: string
label: string | React.ReactElement<any>
description?: string
icon?: IconProps["name"]
icon?: IconProps["name"] | React.ReactElement<any>
iconColor?: keyof OperationalStyleConstants["color"]
onClick?: ContextMenuProps["onClick"]
value?: TValue
Expand Down Expand Up @@ -106,21 +106,35 @@ const Content: React.SFC<{ value: StringOrItem }> = ({ value }) => {
)
}

const InPlaceIcon = (props: Props) =>
typeof props.item !== "string" ? (
<ContextMenuIcon
iconlocation_={props.iconLocation}
color={props.item.iconColor}
left={props.iconLocation === "left" || !props.iconLocation}
name={props.item.icon as IconName}
/>
) : null
const ContextMenuItemIcon: React.SFC<Pick<Props, "item" | "iconLocation">> = props => {
// If item is just a string,
if (typeof props.item === "string") {
return <></>
}

// If it's an object with an icon property
if (typeof props.item.icon === "string") {
return (
<ContextMenuIcon
iconlocation_={props.iconLocation}
color={props.item.iconColor}
left={props.iconLocation === "left" || !props.iconLocation}
name={props.item.icon as IconName}
/>
)
}

// If it's an object with a React Element as a property
return <>{props.item.icon}</>
}

const ContextMenuItem: React.SFC<Props> = props => (
<Container {...props} condensed={props.condensed}>
{(!props.iconLocation || props.iconLocation === "left") && <InPlaceIcon {...props} />}
{(!props.iconLocation || props.iconLocation === "left") && (
<ContextMenuItemIcon iconLocation={props.iconLocation} item={props.item} />
)}
<Content value={props.item} />
{props.iconLocation === "right" && <InPlaceIcon {...props} />}
{props.iconLocation === "right" && <ContextMenuItemIcon iconLocation={props.iconLocation} item={props.item} />}
</Container>
)

Expand Down
58 changes: 58 additions & 0 deletions src/ContextMenu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,64 @@ const menuItems = ["Menu 1", "Menu 2", "Menu 3"]
</ContextMenu>
```

#### Usage with React Nodes as Labels

In some cases, you might want your `label` to be a little bit more clever than just a string. This example shows a `ContextMenu` with a JSX element as its `label`.

```jsx
/* Anything can be a label now, even some DIV you style yourself */
const MyLabelContainer = ({ children, style }) => (
<div style={{ marginRight: 8, padding: "8px 0", ...style }}>{children}</div>
)

const menuItems = [
{
label: (
<MyLabelContainer>
<Contact name="Tejas Kumar" meta="youare@cool.com" />
</MyLabelContainer>
),
icon: "Add",
},
{
label: (
<MyLabelContainer>
<Contact name="Peter Szerzo" meta="peter@norway.com" />
</MyLabelContainer>
),
icon: (
<div style={{ marginLeft: "auto" }}>
<Hint right>User already exists</Hint>
</div>
),
},
{
label: (
<MyLabelContainer>
<Contact name="Sibelius Seraphini" meta="sibelius@seraphini.com" />
</MyLabelContainer>
),
icon: "Add",
},
{
label: (
<MyLabelContainer>
<Contact
name={`Arnold "Governator" Schwarzennegger`}
meta="arnoldgovernatorschwarzennegger@thegovernmentofcalifornia.usa🇺🇸"
/>
</MyLabelContainer>
),
icon: "Add",
},
]
;<>
<ContextMenu iconLocation="right" items={menuItems} onClick={item => alert(`clicked`)}>
<Button>See Users</Button>
</ContextMenu>
</>
```

#### Large number of items

The context menu doesn't grow past a certain maximum height, but scrolls in its container instead.
Expand Down
27 changes: 25 additions & 2 deletions src/Hint/Hint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,46 @@ export interface HintProps extends DefaultProps {
* Indicates that this component is right of other content, and adds an appropriate left margin.
*/
right?: boolean
tooltipPosition?: "left" | "top" | "right" | "bottom" | "smart"
}

const Container = styled("div")<{ left?: HintProps["left"]; right?: HintProps["right"] }>(({ left, right, theme }) => ({
position: "relative",
display: "inline-block",
display: "inline-flex",
verticalAlign: "middle",
alignItems: "center",
color: theme.color.text.lightest,
marginRight: left ? theme.space.base : 0,
marginLeft: right ? theme.space.base : 0,
...hoverTooltip,
}))

const HintTooltip: React.SFC<{ position: HintProps["tooltipPosition"] }> = props => {
switch (props.position) {
case "right":
return <Tooltip right {...props} />
case "top":
return <Tooltip top {...props} />
case "bottom":
return <Tooltip bottom {...props} />
case "left":
return <Tooltip left {...props} />
case "smart":
return <Tooltip smart {...props} />
default:
return null
}
}

const Hint: React.SFC<HintProps> = props => (
<Container {...props}>
<Icon name="Question" size={12} />
<Tooltip right>{props.children}</Tooltip>
<HintTooltip position={props.tooltipPosition!}>{props.children}</HintTooltip>
</Container>
)

Hint.defaultProps = {
tooltipPosition: "left",
}

export default Hint
2 changes: 1 addition & 1 deletion src/TopbarSelect/TopbarSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface TopbarSelectProps {
/** Menu items, conforming to the ContextMenu API */
items: ContextMenuProps["items"]
/** Change handler */
onChange?: (newLabel: string) => void
onChange?: (newLabel: string | React.ReactElement<any>) => void
}

const TopbarSelectContainer = styled("div")`
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export { default as CardSection, CardSectionProps, DragAndDropFeedback } from ".
export { default as Checkbox, CheckboxProps } from "./Checkbox/Checkbox"
export { default as Chip, ChipProps } from "./Chip/Chip"
export { default as Code, CodeProps } from "./Code/Code"
export { default as Contact, ContactProps } from "./Contact/Contact"
export { default as ContextMenu, ContextMenuProps } from "./ContextMenu/ContextMenu"
export { default as DatePicker, DatePickerProps } from "./DatePicker/DatePicker"
export { default as Debug } from "./Debug/Debug"
Expand Down
7 changes: 7 additions & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,19 @@ const separatorColors = {
* A specialized color palette for typography.
*/
const textColors = {
/** `#333` */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍 ❤️

dark: "#333",
/** `#545454` */
default: "#545454",
/** `#c0c0c0` */
disabled: "#c0c0c0",
/** `#666` */
light: "#666",
/** `#747474` */
lighter: "#747474",
/** `#909090` */
lightest: "#909090",
/** primary color */
action: primaryColor,
white: whiteColor,
}
Expand Down