From ce7b84381e74f0eacfc9aaed919317165f534677 Mon Sep 17 00:00:00 2001 From: Austin Sullivan Date: Thu, 14 Apr 2022 14:41:28 -0400 Subject: [PATCH] chore(AlertGroup): convert examples to TypeScript/functional components --- .../AlertGroup/examples/AlertGroup.md | 368 +----------------- .../AlertGroup/examples/AlertGroupAsync.tsx | 73 ++++ .../examples/AlertGroupMultipleDynamic.tsx | 59 +++ .../examples/AlertGroupSingularDynamic.tsx | 70 ++++ .../AlertGroupSingularDynamicOverflow.tsx | 89 +++++ .../AlertGroup/examples/AlertGroupStatic.tsx | 11 + .../AlertGroup/examples/AlertGroupToast.tsx | 69 ++++ .../AlertGroupToastOverflowCapture.tsx | 88 +++++ packages/react-core/src/helpers/index.ts | 1 + .../react-core/src/helpers/useInterval.ts | 25 ++ 10 files changed, 492 insertions(+), 361 deletions(-) create mode 100644 packages/react-core/src/components/AlertGroup/examples/AlertGroupAsync.tsx create mode 100644 packages/react-core/src/components/AlertGroup/examples/AlertGroupMultipleDynamic.tsx create mode 100644 packages/react-core/src/components/AlertGroup/examples/AlertGroupSingularDynamic.tsx create mode 100644 packages/react-core/src/components/AlertGroup/examples/AlertGroupSingularDynamicOverflow.tsx create mode 100644 packages/react-core/src/components/AlertGroup/examples/AlertGroupStatic.tsx create mode 100644 packages/react-core/src/components/AlertGroup/examples/AlertGroupToast.tsx create mode 100644 packages/react-core/src/components/AlertGroup/examples/AlertGroupToastOverflowCapture.tsx create mode 100644 packages/react-core/src/helpers/useInterval.ts diff --git a/packages/react-core/src/components/AlertGroup/examples/AlertGroup.md b/packages/react-core/src/components/AlertGroup/examples/AlertGroup.md index 929c861c04a..001517785f8 100644 --- a/packages/react-core/src/components/AlertGroup/examples/AlertGroup.md +++ b/packages/react-core/src/components/AlertGroup/examples/AlertGroup.md @@ -8,389 +8,35 @@ propComponents: ['Alert', 'AlertGroup', 'AlertActionCloseButton', 'AlertActionLi ## Examples ### Static alert group These alerts appear on page load and are discoverable from within the normal page content flow, and will not be announced individually/explicitly to assistive technology. -```js -import React from 'react'; -import { Alert, AlertGroup } from '@patternfly/react-core'; - -class StaticAlertGroup extends React.Component { - render() { - return ( - - - - - - - ); - } -} +```ts file="./AlertGroupStatic.tsx" ``` ### Toast alert group Alerts asynchronously appended into dynamic AlertGroups with `isLiveRegion` will be announced to assistive technology at the moment the change happens, following the strategy used for aria-atomic, which defaults to false. This means only changes of type "addition" will be announced. -```js -import React from 'react'; -import { Alert, AlertGroup, AlertActionCloseButton, AlertVariant, InputGroup } from '@patternfly/react-core'; -class ToastAlertGroup extends React.Component { - constructor() { - super(); - this.state = { alerts: [] }; - this.addAlert = (title, variant, key) => { - this.setState({ - alerts: [ ...this.state.alerts, { title: title, variant: variant, key }] - }); - }; - this.removeAlert = key => { - this.setState({ alerts: [...this.state.alerts.filter(el => el.key !== key)] }); - }; - } - render() { - const btnClasses = ['pf-c-button', 'pf-m-secondary'].join(' '); - const getUniqueId = () => (new Date().getTime()); - const addSuccessAlert = () => { this.addAlert('Toast success alert', 'success', getUniqueId()) }; - const addDangerAlert = () => { this.addAlert('Toast danger alert', 'danger', getUniqueId()) }; - const addInfoAlert = () => { this.addAlert('Toast info alert', 'info', getUniqueId()) }; - return ( - - - - - - - - {this.state.alerts.map(({key, variant, title}) => ( - this.removeAlert(key)} - /> - } - key={key} /> - ))} - - - ); - } -} +```ts file="./AlertGroupToast.tsx" ``` ### Toast alert group with overflow capture After a specified number of alerts displayed is reached, we will see an overflow message instead of new alerts. Alerts asynchronously appended into dynamic AlertGroups with `isLiveRegion` will be announced to assistive technology at the moment the change happens. When the overflow message appears or is updated in AlertGroups with `isLiveRegion`, the `View 1 more alert` text will be read, but the alert message will not be read. screen reader user or keyboard user will need a way to navigate to and reveal the hidden alerts before they disappear. Alternatively, there should be a place that notifications or alerts are collected to be viewed or read later. In this example we are showing a max of 4 alerts. -```js isBeta -import React from 'react'; -import { Alert, AlertGroup, AlertActionCloseButton, AlertVariant, InputGroup } from '@patternfly/react-core'; -class ToastAlertGroup extends React.Component { - constructor() { - super(); - this.state = { - alerts: [], - maxDisplayed: "4", - overflowMessage: '' - }; - this.getOverflowMessage = (alertsNumber) => { - const overflow = alertsNumber - this.state.maxDisplayed; - if (overflow > 0) { - return "View " + (overflow) + " more alerts"; - } - return ''; - }; - this.addAlert = (title, variant, key) => { - this.setState({ - ...this.state, - alerts: [ ...this.state.alerts, { title: title, variant: variant, key }], - overflowMessage: this.getOverflowMessage(this.state.alerts.length + 1) - }); - }; - this.removeAlert = key => { - const newAlerts = [...this.state.alerts.filter(el => el.key !== key)]; - this.setState({ - ...this.state, - alerts: newAlerts, - overflowMessage: this.getOverflowMessage(newAlerts.length) - }); - }; - } - render() { - const btnClasses = ['pf-c-button', 'pf-m-secondary'].join(' '); - const getUniqueId = () => (new Date().getTime()); - const addSuccessAlert = () => { this.addAlert('Toast success alert', 'success', getUniqueId()) }; - const addDangerAlert = () => { this.addAlert('Toast danger alert', 'danger', getUniqueId()) }; - const addInfoAlert = () => { this.addAlert('Toast info alert', 'info', getUniqueId()) }; - const onOverflowClick = () => { - console.log('Overflow message clicked'); - }; - return ( - - - - - - - - {this.state.alerts.slice(0, this.state.maxDisplayed).map(({key, variant, title}) => ( - this.removeAlert(key)} - /> - } - key={key} /> - ))} - - - ); - } -} +```ts file="AlertGroupToastOverflowCapture.tsx" isBeta ``` ### Singular dynamic alert group This alert will appear in the page, most likely in response to a user action. -```js -import React from 'react'; -import { Alert, AlertGroup, AlertVariant, AlertActionCloseButton, InputGroup } from '@patternfly/react-core'; -class SingularAdditiveAlertGroup extends React.Component { - constructor() { - super(); - this.state = { - alerts: [] - } - } - render() { - const addAlert = (title, variant, key) => { - this.setState({ alerts: [...this.state.alerts, { title: title, variant: variant, key }] }); - }; - const btnClasses = ['pf-c-button', 'pf-m-secondary'].join(' '); - const getUniqueId = () => (new Date().getTime()); - const addSuccessAlert = () => { addAlert('Single success alert', 'success', getUniqueId()) }; - const addDangerAlert = () => { addAlert('Single danger alert', 'danger', getUniqueId()) }; - const addInfoAlert = () => { addAlert('Single info alert', 'info', getUniqueId()) }; - this.removeAlert = key => { - this.setState({ alerts: [...this.state.alerts.filter(el => el.key !== key)] }); - }; - return ( - - - - - - - - {this.state.alerts.map(({ title, variant, key }) => ( - this.removeAlert(key)} - /> - }/> - ))} - - - ); - } -} +```ts file="./AlertGroupSingularDynamic.tsx" ``` ### Singular dynamic alert group with overflow message This alert will appear in the page, most likely in response to a user action. In this example we are showing a max of 4 alerts. -```js isBeta -import React from 'react'; -import { Alert, AlertGroup, AlertVariant, AlertActionCloseButton, InputGroup } from '@patternfly/react-core'; -class SingularAdditiveAlertGroup extends React.Component { - constructor() { - super(); - this.state = { - alerts: [], - maxDisplayed: "4", - overflowMessage: '' - }; - this.getOverflowMessage = (alertsNumber) => { - const overflow = alertsNumber - this.state.maxDisplayed; - if (overflow > 0) { - return "View " + (overflow) + " more alerts"; - } - return ''; - }; - this.addAlert = (title, variant, key) => { - this.setState({ - ...this.state, - alerts: [ ...this.state.alerts, { title: title, variant: variant, key }], - overflowMessage: this.getOverflowMessage(this.state.alerts.length + 1) - }); - }; - this.removeAlert = key => { - const newAlerts = [...this.state.alerts.filter(el => el.key !== key)]; - this.setState({ - ...this.state, - alerts: newAlerts, - overflowMessage: this.getOverflowMessage(newAlerts.length) - }); - }; - } - render() { - const btnClasses = ['pf-c-button', 'pf-m-secondary'].join(' '); - const getUniqueId = () => (new Date().getTime()); - const addSuccessAlert = () => { this.addAlert('Single success alert', 'success', getUniqueId()) }; - const addDangerAlert = () => { this.addAlert('Single danger alert', 'danger', getUniqueId()) }; - const addInfoAlert = () => { this.addAlert('Single info alert', 'info', getUniqueId()) }; - const onOverflowClick = () => { - console.log('Overflow message clicked'); - }; - return ( - - - - - - - - {this.state.alerts.slice(0, this.state.maxDisplayed).map(({ title, variant, key }) => ( - this.removeAlert(key)} - /> - }/> - ))} - - - ); - } -} +```ts file="AlertGroupSingularDynamicOverflow.tsx" isBeta ``` ### Multiple dynamic alert group These alerts will appear in the page, most likely in response to a user action. -```js -import React from 'react'; -import { Alert, AlertGroup, AlertVariant, AlertActionCloseButton, InputGroup } from '@patternfly/react-core'; -class MultipleAdditiveAlertGroup extends React.Component { - constructor() { - super(); - this.state = { - alerts: [] - } - } - render() { - const addAlerts = (incomingAlerts) => { - this.setState({ alerts: [...this.state.alerts, ...incomingAlerts] }); - }; - const getUniqueId = () => ( - (String.fromCharCode(65 + Math.floor(Math.random() * 26))+ Date.now()) - ); - const btnClasses = ['pf-c-button', 'pf-m-secondary'].join(' '); - const addAlertCollection = () => { - addAlerts([ - { title: 'First alert notification.', variant: 'success', key: getUniqueId() }, - { title: 'Second alert notification.', variant: 'warning', key: getUniqueId() }, - { title: 'Third alert notification.', variant: 'danger', key: getUniqueId() } - ]) - }; - this.removeAlert = key => { - this.setState({ alerts: [...this.state.alerts.filter(el => el.key !== key)] }); - }; - return ( - - - - - - {this.state.alerts.map(({ title, variant, key, action }) => ( - this.removeAlert(key)} - /> - }/> - ))} - - - ); - } -} +```ts file="./AlertGroupMultipleDynamic.tsx" ``` ### Async alert group This shows how an alert could be triggered by an asynchronous event in the application. Note how you can customize how the alert will be announced to assistive technology. See the alert group accessibility tab for more information. -```js -import React from 'react'; -import { Alert, AlertGroup, AlertActionCloseButton, AlertVariant, InputGroup } from '@patternfly/react-core'; -class AsyncAdditiveAlertGroup extends React.Component { - constructor() { - super(); - this.state = { - alerts: [], - timer: null - } - this.stopAsyncAlerts = () => { clearInterval(this.state.timer); } - } - componentWillUnmount() { this.stopAsyncAlerts(); } - render() { - const addAlerts = (incomingAlerts) => { this.setState({ alerts: [...this.state.alerts, ...incomingAlerts] }); }; - const getUniqueId = () => (new Date().getTime()); - const btnClasses = ['pf-c-button', 'pf-m-secondary'].join(' '); - this.removeAlert = key => { - this.setState({ alerts: [...this.state.alerts.filter(el => el.key !== key)] }); - }; - const startAsyncAlerts = () => { - let timerValue = setInterval(() => { - addAlerts([ - { - title: `Async notification ${this.state.alerts.length + 1} was added to the queue.`, - variant: 'danger', - key: getUniqueId() - } - ]) - }, 4500); - this.setState({timer: timerValue}); - }; - return ( - - - - - - - {this.state.alerts.map(({ title, variant, key }) => ( - this.removeAlert(key)} - /> - }/> - ))} - - - ); - } -} +```ts file="./AlertGroupAsync.tsx" ``` diff --git a/packages/react-core/src/components/AlertGroup/examples/AlertGroupAsync.tsx b/packages/react-core/src/components/AlertGroup/examples/AlertGroupAsync.tsx new file mode 100644 index 00000000000..aa1f4f785c7 --- /dev/null +++ b/packages/react-core/src/components/AlertGroup/examples/AlertGroupAsync.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { + Alert, + AlertProps, + AlertGroup, + AlertActionCloseButton, + AlertVariant, + InputGroup, + useInterval +} from '@patternfly/react-core'; + +export const AlertGroupAsync: React.FunctionComponent = () => { + const [alerts, setAlerts] = React.useState[]>([]); + const [isRunning, setIsRunning] = React.useState(false); + + const btnClasses = ['pf-c-button', 'pf-m-secondary'].join(' '); + + const getUniqueId = () => new Date().getTime(); + + const addAlert = () => { + setAlerts(prevAlerts => [ + ...prevAlerts, + { + title: `Async notification ${prevAlerts.length + 1} was added to the queue.`, + variant: 'danger', + key: getUniqueId() + } + ]); + }; + + const removeAlert = (key: React.Key) => { + setAlerts(prevAlerts => [...prevAlerts.filter(alert => alert.key !== key)]); + }; + + const startAsyncAlerts = () => { + setIsRunning(true); + }; + + const stopAsyncAlerts = () => { + setIsRunning(false); + }; + + useInterval(addAlert, isRunning ? 4500 : null); + + return ( + + + + + + + {alerts.map(({ title, variant, key }) => ( + removeAlert(key)} + /> + } + /> + ))} + + + ); +}; diff --git a/packages/react-core/src/components/AlertGroup/examples/AlertGroupMultipleDynamic.tsx b/packages/react-core/src/components/AlertGroup/examples/AlertGroupMultipleDynamic.tsx new file mode 100644 index 00000000000..e001739dfe8 --- /dev/null +++ b/packages/react-core/src/components/AlertGroup/examples/AlertGroupMultipleDynamic.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { + Alert, + AlertProps, + AlertGroup, + AlertActionCloseButton, + AlertVariant, + InputGroup +} from '@patternfly/react-core'; + +export const AlertGroupMultipleDynamic: React.FunctionComponent = () => { + const [alerts, setAlerts] = React.useState[]>([]); + + const addAlerts = (incomingAlerts: Partial[]) => { + setAlerts(prevAlerts => [...prevAlerts, ...incomingAlerts]); + }; + + const removeAlert = (key: React.Key) => { + setAlerts(prevAlerts => [...prevAlerts.filter(alert => alert.key !== key)]); + }; + + const btnClasses = ['pf-c-button', 'pf-m-secondary'].join(' '); + + const getUniqueId = () => String.fromCharCode(65 + Math.floor(Math.random() * 26)) + Date.now(); + + const addAlertCollection = () => { + addAlerts([ + { title: 'First alert notification.', variant: 'success', key: getUniqueId() }, + { title: 'Second alert notification.', variant: 'warning', key: getUniqueId() }, + { title: 'Third alert notification.', variant: 'danger', key: getUniqueId() } + ]); + }; + + return ( + + + + + + {alerts.map(({ title, variant, key }) => ( + removeAlert(key)} + /> + } + /> + ))} + + + ); +}; diff --git a/packages/react-core/src/components/AlertGroup/examples/AlertGroupSingularDynamic.tsx b/packages/react-core/src/components/AlertGroup/examples/AlertGroupSingularDynamic.tsx new file mode 100644 index 00000000000..f6264c311d2 --- /dev/null +++ b/packages/react-core/src/components/AlertGroup/examples/AlertGroupSingularDynamic.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { + Alert, + AlertProps, + AlertGroup, + AlertActionCloseButton, + AlertVariant, + InputGroup +} from '@patternfly/react-core'; + +export const AlertGroupSingularDynamic: React.FunctionComponent = () => { + const [alerts, setAlerts] = React.useState[]>([]); + + const addAlert = (title: string, variant: AlertProps['variant'], key: React.Key) => { + setAlerts(prevAlerts => [...prevAlerts, { title, variant, key }]); + }; + + const removeAlert = (key: React.Key) => { + setAlerts(prevAlerts => [...prevAlerts.filter(alert => alert.key !== key)]); + }; + + const btnClasses = ['pf-c-button', 'pf-m-secondary'].join(' '); + + const getUniqueId = () => new Date().getTime(); + + const addSuccessAlert = () => { + addAlert('Toast success alert', 'success', getUniqueId()); + }; + + const addDangerAlert = () => { + addAlert('Toast danger alert', 'danger', getUniqueId()); + }; + + const addInfoAlert = () => { + addAlert('Toast info alert', 'info', getUniqueId()); + }; + + return ( + + + + + + + + {alerts.map(({ title, variant, key }) => ( + removeAlert(key)} + /> + } + /> + ))} + + + ); +}; diff --git a/packages/react-core/src/components/AlertGroup/examples/AlertGroupSingularDynamicOverflow.tsx b/packages/react-core/src/components/AlertGroup/examples/AlertGroupSingularDynamicOverflow.tsx new file mode 100644 index 00000000000..48bd0567a68 --- /dev/null +++ b/packages/react-core/src/components/AlertGroup/examples/AlertGroupSingularDynamicOverflow.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import { + Alert, + AlertProps, + AlertGroup, + AlertActionCloseButton, + AlertVariant, + InputGroup +} from '@patternfly/react-core'; + +export const AlertGroupSingularDynamicOverflow: React.FunctionComponent = () => { + const [alerts, setAlerts] = React.useState[]>([]); + const [overflowMessage, setOverflowMessage] = React.useState(''); + + const maxDisplayed = 4; + + const getOverflowMessage = (alertsNumber: number) => { + const overflow = alertsNumber - maxDisplayed; + if (overflow > 0) { + return `View ${overflow} more alerts`; + } + return ''; + }; + + const addAlert = (title: string, variant: AlertProps['variant'], key: React.Key) => { + setAlerts(prevAlerts => [...prevAlerts, { title, variant, key }]); + setOverflowMessage(getOverflowMessage(alerts.length + 1)); + }; + + const removeAlert = (key: React.Key) => { + const newAlerts = alerts.filter(alert => alert.key !== key); + setAlerts(newAlerts); + setOverflowMessage(getOverflowMessage(newAlerts.length)); + }; + + const btnClasses = ['pf-c-button', 'pf-m-secondary'].join(' '); + + const getUniqueId = () => new Date().getTime(); + + const addSuccessAlert = () => { + addAlert('Toast success alert', 'success', getUniqueId()); + }; + + const addDangerAlert = () => { + addAlert('Toast danger alert', 'danger', getUniqueId()); + }; + + const addInfoAlert = () => { + addAlert('Toast info alert', 'info', getUniqueId()); + }; + + const onOverflowClick = () => { + // eslint-disable-next-line no-console + console.log('Overflow message clicked'); + }; + + return ( + + + + + + + + {alerts.slice(0, maxDisplayed).map(({ key, variant, title }) => ( + removeAlert(key)} + /> + } + key={key} + /> + ))} + + + ); +}; diff --git a/packages/react-core/src/components/AlertGroup/examples/AlertGroupStatic.tsx b/packages/react-core/src/components/AlertGroup/examples/AlertGroupStatic.tsx new file mode 100644 index 00000000000..c0afd20e980 --- /dev/null +++ b/packages/react-core/src/components/AlertGroup/examples/AlertGroupStatic.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { Alert, AlertGroup } from '@patternfly/react-core'; + +export const AlertGroupStatic: React.FunctionComponent = () => ( + + + + + + +); diff --git a/packages/react-core/src/components/AlertGroup/examples/AlertGroupToast.tsx b/packages/react-core/src/components/AlertGroup/examples/AlertGroupToast.tsx new file mode 100644 index 00000000000..59ce250fcc4 --- /dev/null +++ b/packages/react-core/src/components/AlertGroup/examples/AlertGroupToast.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { + Alert, + AlertProps, + AlertGroup, + AlertActionCloseButton, + AlertVariant, + InputGroup +} from '@patternfly/react-core'; + +export const AlertGroupToast: React.FunctionComponent = () => { + const [alerts, setAlerts] = React.useState[]>([]); + + const addAlert = (title: string, variant: AlertProps['variant'], key: React.Key) => { + setAlerts(prevAlerts => [...prevAlerts, { title, variant, key }]); + }; + + const removeAlert = (key: React.Key) => { + setAlerts(prevAlerts => [...prevAlerts.filter(alert => alert.key !== key)]); + }; + + const btnClasses = ['pf-c-button', 'pf-m-secondary'].join(' '); + + const getUniqueId = () => new Date().getTime(); + + const addSuccessAlert = () => { + addAlert('Toast success alert', 'success', getUniqueId()); + }; + + const addDangerAlert = () => { + addAlert('Toast danger alert', 'danger', getUniqueId()); + }; + + const addInfoAlert = () => { + addAlert('Toast info alert', 'info', getUniqueId()); + }; + + return ( + + + + + + + + {alerts.map(({ key, variant, title }) => ( + removeAlert(key)} + /> + } + key={key} + /> + ))} + + + ); +}; diff --git a/packages/react-core/src/components/AlertGroup/examples/AlertGroupToastOverflowCapture.tsx b/packages/react-core/src/components/AlertGroup/examples/AlertGroupToastOverflowCapture.tsx new file mode 100644 index 00000000000..2ea68546777 --- /dev/null +++ b/packages/react-core/src/components/AlertGroup/examples/AlertGroupToastOverflowCapture.tsx @@ -0,0 +1,88 @@ +import React from 'react'; +import { + Alert, + AlertProps, + AlertGroup, + AlertActionCloseButton, + AlertVariant, + InputGroup +} from '@patternfly/react-core'; + +export const AlertGroupToastOverflowCapture: React.FunctionComponent = () => { + const [alerts, setAlerts] = React.useState[]>([]); + const [overflowMessage, setOverflowMessage] = React.useState(''); + + const maxDisplayed = 4; + + const getOverflowMessage = (alertsNumber: number) => { + const overflow = alertsNumber - maxDisplayed; + if (overflow > 0) { + return `View ${overflow} more alerts`; + } + return ''; + }; + + const addAlert = (title: string, variant: AlertProps['variant'], key: React.Key) => { + setAlerts(prevAlerts => [...prevAlerts, { title, variant, key }]); + setOverflowMessage(getOverflowMessage(alerts.length + 1)); + }; + + const removeAlert = (key: React.Key) => { + const newAlerts = alerts.filter(alert => alert.key !== key); + setAlerts(newAlerts); + setOverflowMessage(getOverflowMessage(newAlerts.length)); + }; + + const btnClasses = ['pf-c-button', 'pf-m-secondary'].join(' '); + + const getUniqueId = () => new Date().getTime(); + + const addSuccessAlert = () => { + addAlert('Toast success alert', 'success', getUniqueId()); + }; + + const addDangerAlert = () => { + addAlert('Toast danger alert', 'danger', getUniqueId()); + }; + + const addInfoAlert = () => { + addAlert('Toast info alert', 'info', getUniqueId()); + }; + + const onOverflowClick = () => { + // eslint-disable-next-line no-console + console.log('Overflow message clicked'); + }; + + return ( + + + + + + + + {alerts.slice(0, maxDisplayed).map(({ key, variant, title }) => ( + removeAlert(key)} + /> + } + key={key} + /> + ))} + + + ); +}; diff --git a/packages/react-core/src/helpers/index.ts b/packages/react-core/src/helpers/index.ts index 80415b12ab6..d75c87f9765 100644 --- a/packages/react-core/src/helpers/index.ts +++ b/packages/react-core/src/helpers/index.ts @@ -9,3 +9,4 @@ export * from './Popper/Popper'; export * from './useIsomorphicLayout'; export * from './KeyboardHandler'; export * from './resizeObserver'; +export * from './useInterval'; diff --git a/packages/react-core/src/helpers/useInterval.ts b/packages/react-core/src/helpers/useInterval.ts new file mode 100644 index 00000000000..39a29dc722c --- /dev/null +++ b/packages/react-core/src/helpers/useInterval.ts @@ -0,0 +1,25 @@ +import * as React from 'react'; + +/** This is a custom React hook in a format suggest by Dan Abramov in a blog post here: + * https://overreacted.io/making-setinterval-declarative-with-react-hooks/. It allows setInterval to be used + * declaratively in functional React components. + */ + +export function useInterval(callback: () => void, delay: number | null) { + const savedCallback = React.useRef(() => {}); + + React.useEffect(() => { + savedCallback.current = callback; + }, [callback]); + + React.useEffect(() => { + function tick() { + savedCallback.current(); + } + + if (delay !== null) { + const id = setInterval(tick, delay); + return () => clearInterval(id); + } + }, [delay]); +}