Skip to content

Commit

Permalink
**Breaking:** Fix operational context (#921)
Browse files Browse the repository at this point in the history
* Make operational-context class compatible

* Remove windowSize in context and usages

* Add useWindowSize for later :)

* Refactored Tooltip with hooks, didn't change logic

* Make styleguidist recognize React component in Tooltip.tsx

* Coerce colors to strings for safety

* Move windowsize hook

* Move useURLState hook

* Move hooks

* Move context examples

* Fix test regex matcher

* Fix onTabChange warning
  • Loading branch information
fabien0102 authored and Tejas Kumar committed Feb 15, 2019
1 parent 80ac447 commit d9ab7e5
Show file tree
Hide file tree
Showing 18 changed files with 251 additions and 245 deletions.
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

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

0 comments on commit d9ab7e5

Please sign in to comment.