-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(common): Implement the widget configuration mode which renders t…
…he exported `ConfigControls` for every widget. Closes #562
- Loading branch information
Showing
14 changed files
with
714 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
128 changes: 128 additions & 0 deletions
128
...-common/src/components/pages/dashboard-page/dashboard/widget-renderer/config-renderer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/* eslint-disable @typescript-eslint/ban-ts-comment */ | ||
import { Dispatch, SetStateAction, useCallback, useState } from 'react'; | ||
import { GenericProps, Widget } from '@wuespace/telestion-client-types'; | ||
|
||
import { OverflowFix } from '../../../../widget-helper'; | ||
import { usePropsClipboard } from '../../props-clipboard'; | ||
import { | ||
ConfigContainer, | ||
ConfigHeader, | ||
ConfigFooter, | ||
CopyPasteActions | ||
} from './config'; | ||
|
||
/** | ||
* Return type of the {@link useState} hook | ||
* specified for the {@link GenericProps} state. | ||
*/ | ||
export type PropsState = [GenericProps, Dispatch<SetStateAction<GenericProps>>]; | ||
|
||
/** | ||
* React Props of {@link ConfigRenderer} | ||
* | ||
* For more information about React Props, please look here: | ||
* {@link https://reactjs.org/docs/components-and-props.html} | ||
* | ||
* @see {@link ConfigRenderer} | ||
* @see {@link https://reactjs.org/docs/components-and-props.html} | ||
*/ | ||
export interface ConfigRendererProps { | ||
/** | ||
* The actual widget which contains the components and settings. | ||
*/ | ||
widget: Widget; | ||
|
||
/** | ||
* The unique identifier of the widget in the dashboard configuration. | ||
*/ | ||
id: string; | ||
|
||
/** | ||
* The entire props state from {@link useState}. | ||
*/ | ||
propsState: PropsState; | ||
|
||
/** | ||
* Close the configuration mode. | ||
*/ | ||
onClose: () => void; | ||
} | ||
|
||
/** | ||
* Renders the widget configuration and wrapper with header and footer. | ||
* It implements the copy and paste for widget properties. | ||
* | ||
* @see {@link ConfigRendererProps} | ||
* @see {@link WidgetRenderer} | ||
* @see {@link Widget} | ||
* | ||
* @throws Error - if the ConfigControls are not defined | ||
* | ||
* @example | ||
* ```tsx | ||
* // build up state | ||
* const [inConfig, open, close] = useBooleanState(); | ||
* const propsState = useStoredState(`${id}-${version}`, initialProps || {}); | ||
* | ||
* return inConfig ? ( | ||
* <ConfigRenderer | ||
* widget={widget} | ||
* id={id} | ||
* propsState={propsState} | ||
* onClose={close} | ||
* /> | ||
* ) : ( | ||
* <Content {...propsState[0]} /> | ||
* ) | ||
* ``` | ||
*/ | ||
export function ConfigRenderer({ | ||
widget, | ||
id, | ||
propsState, | ||
onClose | ||
}: ConfigRendererProps) { | ||
const { name, title, ConfigControls } = widget; | ||
const [global, setGlobal] = propsState; | ||
const [clipboard, setClipboard] = usePropsClipboard(name); | ||
const [local, setLocal] = useState(global); | ||
|
||
const copy = () => setClipboard(local); | ||
|
||
const paste = useCallback(() => { | ||
if (clipboard) setLocal(clipboard); | ||
}, [clipboard]); | ||
|
||
const abort = useCallback(() => { | ||
setLocal(global); | ||
onClose(); | ||
}, [global, onClose]); | ||
|
||
const confirm = () => { | ||
setGlobal(local); | ||
onClose(); | ||
}; | ||
|
||
const update = useCallback((newProps: Partial<GenericProps>) => { | ||
setLocal(prevState => ({ ...prevState, ...newProps })); | ||
}, []); | ||
|
||
if (!ConfigControls) throw new Error('Config Controls are not defined'); | ||
|
||
return ( | ||
<ConfigContainer> | ||
<ConfigHeader title={title || name} id={id}> | ||
<CopyPasteActions | ||
isPasteDisabled={!!clipboard} | ||
onCopy={copy} | ||
onPaste={paste} | ||
/> | ||
</ConfigHeader> | ||
<OverflowFix flexShrink={1} flexGrow={1}> | ||
{/* @ts-ignore */} | ||
<ConfigControls currentProps={local} onUpdate={update} /> | ||
</OverflowFix> | ||
<ConfigFooter onAbort={abort} onConfirm={confirm} /> | ||
</ConfigContainer> | ||
); | ||
} |
42 changes: 42 additions & 0 deletions
42
...src/components/pages/dashboard-page/dashboard/widget-renderer/config/config-container.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { ReactElement } from 'react'; | ||
import { Flex, View } from '@adobe/react-spectrum'; | ||
|
||
/** | ||
* React Props of {@link ConfigContainer} | ||
* | ||
* For more information about React Props, please look here: | ||
* {@link https://reactjs.org/docs/components-and-props.html} | ||
* | ||
* @see {@link ConfigContainer} | ||
* @see {@link https://reactjs.org/docs/components-and-props.html} | ||
*/ | ||
export interface ConfigContainerProps { | ||
children: ReactElement | ReactElement[]; | ||
} | ||
|
||
/** | ||
* This is the container element for the widget configuration. | ||
* @param children - the container elements | ||
* | ||
* @example | ||
* ```tsx | ||
* function WidgetRenderer() { | ||
* return inConfig ? ( | ||
* <ConfigContainer> | ||
* {...elements} | ||
* </ConfigContainer> | ||
* ) : ( | ||
* <Content {...props}> | ||
* ); | ||
* } | ||
* ``` | ||
*/ | ||
export function ConfigContainer({ children }: ConfigContainerProps) { | ||
return ( | ||
<View width="100%" height="100%"> | ||
<Flex direction="column" width="100%" height="100%"> | ||
{children} | ||
</Flex> | ||
</View> | ||
); | ||
} |
61 changes: 61 additions & 0 deletions
61
...on/src/components/pages/dashboard-page/dashboard/widget-renderer/config/config-footer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { View, ButtonGroup, Button } from '@adobe/react-spectrum'; | ||
|
||
/** | ||
* React Props of {@link ConfigFooter} | ||
* | ||
* For more information about React Props, please look here: | ||
* {@link https://reactjs.org/docs/components-and-props.html} | ||
* | ||
* @see {@link ConfigFooter} | ||
* @see {@link https://reactjs.org/docs/components-and-props.html} | ||
*/ | ||
export interface ConfigFooterProps { | ||
/** | ||
* Gets called, when the user presses the abort button. | ||
*/ | ||
onAbort: () => void; | ||
|
||
/** | ||
* Gets called, when the user presses the confirm button. | ||
*/ | ||
onConfirm: () => void; | ||
} | ||
|
||
/** | ||
* Renders the widget configuration footer. | ||
* | ||
* It contains the confirm and cancel/abort actions | ||
* the user can take to exit the widget configuration mode. | ||
* | ||
* @see {@link ConfigFooterProps} | ||
* @see {@link ConfigContainer} | ||
* @see {@link WidgetRenderer} | ||
* | ||
* @example | ||
* ```tsx | ||
* function WidgetRenderer() { | ||
* return inConfig ? ( | ||
* <ConfigContainer> | ||
* {...elements} | ||
* <ConfigFooter /> | ||
* </ConfigContainer> | ||
* ) : ( | ||
* <Content {...props}> | ||
* ); | ||
* } | ||
* ``` | ||
*/ | ||
export function ConfigFooter({ onAbort, onConfirm }: ConfigFooterProps) { | ||
return ( | ||
<View flexShrink={0} width="100%" paddingX="size-200" paddingY="size-100"> | ||
<ButtonGroup align="end"> | ||
<Button variant="secondary" onPress={onAbort}> | ||
Cancel | ||
</Button> | ||
<Button variant="cta" onPress={onConfirm} autoFocus> | ||
Confirm | ||
</Button> | ||
</ButtonGroup> | ||
</View> | ||
); | ||
} |
86 changes: 86 additions & 0 deletions
86
...on/src/components/pages/dashboard-page/dashboard/widget-renderer/config/config-header.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { View, Divider, Flex, Heading } from '@adobe/react-spectrum'; | ||
|
||
/** | ||
* React Props of {@link ConfigHeader} | ||
* | ||
* For more information about React Props, please look here: | ||
* {@link https://reactjs.org/docs/components-and-props.html} | ||
* | ||
* @see {@link ConfigHeader} | ||
* @see {@link https://reactjs.org/docs/components-and-props.html} | ||
*/ | ||
export interface ConfigHeaderProps { | ||
/** | ||
* The title/widgetName of the widget. | ||
*/ | ||
title: string; | ||
|
||
/** | ||
* The unique identifier of the widget in the dashboard configuration. | ||
*/ | ||
id: string; | ||
|
||
/** | ||
* Additional elements that should be rendered next to the heading | ||
* in a flexbox row. | ||
*/ | ||
// Ask the react-spectrum team for bad typings. :/ | ||
children: any; | ||
} | ||
|
||
/** | ||
* Renders the widget configuration header. | ||
* | ||
* Here are the widget name/title and the id displayed. | ||
* | ||
* The action controls allows the user | ||
* to copy and paste the configuration from one widget to another. | ||
* | ||
* @see {@link ConfigHeaderProps} | ||
* @see {@link ConfigContainer} | ||
* @see {@link WidgetRenderer} | ||
* | ||
* @example | ||
* ```tsx | ||
* function WidgetRenderer() { | ||
* return inConfig ? ( | ||
* <ConfigContainer> | ||
* <ConfigHeader /> | ||
* {...elements} | ||
* </ConfigContainer> | ||
* ) : ( | ||
* <Content {...props}> | ||
* ); | ||
* } | ||
* ``` | ||
*/ | ||
export function ConfigHeader({ title, id, children }: ConfigHeaderProps) { | ||
return ( | ||
<> | ||
<View flexShrink={0} width="100%" paddingX="size-200" paddingY="size-100"> | ||
<Flex | ||
direction="row" | ||
width="100%" | ||
justifyContent="space-between" | ||
alignItems="center" | ||
> | ||
<Flex direction="row"> | ||
<Heading | ||
flexGrow={0} | ||
level={3} | ||
margin="size-200" | ||
marginBottom="size-100" | ||
> | ||
{title} | ||
</Heading> | ||
<div>{id}</div> | ||
</Flex> | ||
|
||
{children} | ||
</Flex> | ||
</View> | ||
|
||
<Divider size="S" /> | ||
</> | ||
); | ||
} |
Oops, something went wrong.