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

**Breaking:** Fix operational context #921

Merged
merged 12 commits into from
Feb 15, 2019
Merged
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"roots": [
"<rootDir>/src"
],
"testRegex": "/__tests__/.*\\.(ts|tsx|js|jsx)$",
"testRegex": "/.*\\.test\\.(ts|tsx|js|jsx)$",
"setupTestFrameworkScriptFile": "jest-enzyme",
"snapshotSerializers": [
"enzyme-to-json/serializer"
Expand Down
6 changes: 4 additions & 2 deletions src/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,12 @@ function Card<T extends {}>(props: CardProps<T>) {
}

if (isWithTabs(props)) {
const { onTabChange, ...otherProps } = props

return (
<Tabs tabs={props.tabs} activeTabName={props.activeTabName} onTabChange={props.onTabChange}>
<Tabs tabs={props.tabs} activeTabName={props.activeTabName} onTabChange={onTabChange}>
{({ tabsBar, activeChildren }) => (
<Container {...rest}>
<Container {...otherProps}>
<CardHeader
title={
<TabsBarContainer>
Expand Down
12 changes: 8 additions & 4 deletions src/Code/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,17 @@ document.body.innerHTML = greeter(user);`}</Code>
Sometimes, you might need to quickly copy a code snippet. Here's how.

```jsx
<OperationalContext>
{({ pushMessage }) => (
const { useOperationalContext } = require("../OperationalContext/OperationalContext")
const ComponentWithCode = () => {
const { pushMessage } = useOperationalContext()
return (
<Code
syntax="typescript"
onCopy={() => pushMessage({ type: "info", body: "Successfully Copied!" })}
copyable
>{`Tuuvaquae5ieroeba5eu1Dae`}</Code>
)}
</OperationalContext>
)
}

;<ComponentWithCode />
```
2 changes: 1 addition & 1 deletion src/Input/Input.Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from "react"
import CopyToClipboard from "react-copy-to-clipboard"

import Icon, { IconName } from "../Icon/Icon"
import OperationalContext from "../OperationalContext/OperationalContext.init"
import OperationalContext from "../OperationalContext/OperationalContext"
import styled from "../utils/styled"
import { height } from "./Input.constants"

Expand Down
50 changes: 0 additions & 50 deletions src/OperationalContext/OperationalContext.init.tsx

This file was deleted.

38 changes: 28 additions & 10 deletions src/OperationalContext/OperationalContext.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,38 @@
import * as React from "react"

import { Context, IMessage, MessageType, useOperationalContext, WindowSize } from "./OperationalContext.init"
export type MessageType = "info" | "success" | "error"

export interface Props {
children: (operationalContext: Context) => undefined | React.ReactNode
export interface IMessage {
body: string
type: MessageType
onClick?: () => void
}

export interface Context {
pushState?: (url: string) => void
replaceState?: (url: string) => void
pushMessage: (message: IMessage) => void
loading: boolean
setLoading: (isLoading: boolean) => void
}

/**
* This component simply wraps OperationalContext in order to allow styleguidist to pick up on
* it and display it in the documentation page.
* Defining a default context value here, used below when instantiating
* the context consumer and provider below in order for context to be
* correctly detected throughout the application.
*/
const OperationalContext: React.SFC<Props> = props => {
const ctx = useOperationalContext()
return <>{props.children({ ...ctx })}</>
const defaultContext: Context = {
pushState: undefined,
replaceState: undefined,
pushMessage: (_: IMessage) => void 0,
loading: false,
setLoading: (_: boolean) => void 0,
}

export default OperationalContext
const ctx = React.createContext(defaultContext)

export { Context, WindowSize, IMessage, MessageType, useOperationalContext }
export const { Consumer: OperationalContext, Provider } = ctx

export const useOperationalContext = () => React.useContext(ctx)

export default OperationalContext
51 changes: 13 additions & 38 deletions src/OperationalContext/README.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,21 @@
The `OperationalContext` component provides utility methods and data that can be used inside `OperationalUI`:

### Window size
stereobooster marked this conversation as resolved.
Show resolved Hide resolved

Child components can get access to window dimensions as follows:

```jsx
<OperationalContext>
{operationalContext => (
<p>{`The viewport is ${operationalContext.windowSize.width} pixels wide and ${
operationalContext.windowSize.height
} tall.`}</p>
)}
</OperationalContext>
```

### OperationalContext Hook
### Messages and Loaders

You can get the value of `OperationalContext` in the shape of context.
You can use `OperationalUI`'s flash- and progress bar features to automatically render and manage these universal UI elements using the `pushMessage` and `setLoadingState` methods provided in context, as shown in the code snippet below:

```jsx
const { useOperationalContext } = require("./OperationalContext")
const ILookPretty = () => {
const ctx = useOperationalContext()
return (
<p>
The viewport is {ctx.windowSize.width} pixels wide and {ctx.windowSize.height} tall.
</p>
)
}
;<ILookPretty />
```

### Messages and Loaders
const ComponentWithContext = () => {
const { pushMessage, setLoading, loading } = useOperationalContext()

You can use `OperationalUI`'s flash- and progress bar features to automatically render and manage these universal UI elements using the `pushMessage` and `setLoadingState` methods provided in context, as shown in the code snippet below:

```jsx
<OperationalContext>
{operationalContext => (
return (
<div style={{ padding: 20 }}>
<Button
color="primary"
onClick={() => {
operationalContext.pushMessage({
pushMessage({
body: "Info message",
type: "info",
})
Expand All @@ -53,7 +26,7 @@ You can use `OperationalUI`'s flash- and progress bar features to automatically
<Button
color="success"
onClick={() => {
operationalContext.pushMessage({
pushMessage({
body: "Success message",
type: "success",
})
Expand All @@ -64,7 +37,7 @@ You can use `OperationalUI`'s flash- and progress bar features to automatically
<Button
color="error"
onClick={() => {
operationalContext.pushMessage({
pushMessage({
body: "Error message",
type: "error",
})
Expand All @@ -74,12 +47,14 @@ You can use `OperationalUI`'s flash- and progress bar features to automatically
</Button>
<Button
onClick={() => {
operationalContext.setLoading(!operationalContext.loading)
setLoading(!loading)
}}
>
Toggle loading state
</Button>
</div>
)}
</OperationalContext>
)
}

;<ComponentWithContext />
```
29 changes: 1 addition & 28 deletions src/OperationalUI/OperationalUI.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { injectGlobal } from "emotion"
import { ThemeProvider } from "emotion-theming"
import { Cancelable } from "lodash"
import debounce from "lodash/debounce"
import merge from "lodash/merge"
import * as React from "react"

import ErrorBoundary from "../Internals/ErrorBoundary"
import Message from "../Internals/Message/Message"
import Messages from "../Internals/Messages/Messages"
import { IMessage, MessageType, WindowSize } from "../OperationalContext/OperationalContext"
import { Provider } from "../OperationalContext/OperationalContext.init"
import { IMessage, MessageType, Provider } from "../OperationalContext/OperationalContext"
import Progress from "../Progress/Progress"
import { darken, DeepPartial } from "../utils"
import constants, { OperationalStyleConstants } from "../utils/constants"
Expand Down Expand Up @@ -46,7 +43,6 @@ export interface OperationalUIProps {
}

export interface State {
windowSize: WindowSize
messages: Array<{
message: IMessage
addedAt: number
Expand Down Expand Up @@ -117,10 +113,6 @@ class OperationalUI extends React.Component<OperationalUIProps, State> {
}

public state: State = {
windowSize: {
width: 0,
height: 0,
},
messages: [],
isLoading: false,
}
Expand Down Expand Up @@ -152,25 +144,10 @@ class OperationalUI extends React.Component<OperationalUIProps, State> {
}
}

/**
* Explicit typing is required here in order to give the typescript compiler access to typings
* used to work out type definitions for the debounce method.
* @todo look into making this unnecessary.
*/
public handleResize: (() => void) & Cancelable = debounce(() => {
this.onSetWindowSize()
}, 200)

public setLoading = (isLoading: boolean) => {
this.setState(() => ({ isLoading }))
}

public onSetWindowSize = () => {
this.setState(() => ({
windowSize: { width: window.innerWidth, height: window.innerHeight },
}))
}

public componentDidCatch(error: Error) {
this.setState({ error })
if (this.props.onError) {
Expand All @@ -182,15 +159,12 @@ class OperationalUI extends React.Component<OperationalUIProps, State> {
if (!this.props.noBaseStyles) {
injectGlobal(baseStylesheet(constants))
}
this.onSetWindowSize()
window.addEventListener("resize", this.handleResize)
}

public componentWillUnmount() {
if (this.messageTimerInterval) {
clearInterval(this.messageTimerInterval)
}
window.removeEventListener("resize", this.handleResize)
}

public render() {
Expand All @@ -207,7 +181,6 @@ class OperationalUI extends React.Component<OperationalUIProps, State> {
pushMessage: this.pushMessage,
loading: this.state.isLoading,
setLoading: this.setLoading,
windowSize: this.state.windowSize,
}}
>
<Container>
Expand Down
Loading