Skip to main content

Adding configuration options

In this tutorial, you'll add configuration options to the widget from the previous tutorials.

Prerequisites

To complete this tutorial, you should be familiar with TypeScript and React.

You should also have the widget's code from Connecting with the event bus.

What you'll build

In this tutorial, you'll add a isReadonly configuration option to your widget. You'll also add a user interface to let Ground Station Operators configure your widget without having to adjust the code.

Link to
src/widgets/my-first-widget/model.ts
import { GenericProps } from '@wuespace/telestion-client-types';

/**
* Props for the "my-first-widget" widget
*/
export interface WidgetProps extends GenericProps {
/**
* Whether the widget should be read-only. If `true`, the reset buttons get disabled.
*/
isReadonly: boolean;
}
src/widgets/my-first-widget/config.tsx
import { Checkbox, View } from '@adobe/react-spectrum';
import { BaseConfigControlsProps } from '@wuespace/telestion-client-types';
import { WidgetProps } from './model';

export function ConfigControls(props: BaseConfigControlsProps<WidgetProps>) {
return (
<View padding="size-200">
<Checkbox
isSelected={props.currentProps.isReadonly}
onChange={isReadonly => props.onUpdate({ isReadonly })}
>
Readonly (disable buttons)
</Checkbox>
</View>
);
}
src/widgets/my-first-widget/index.ts
import { Widget } from '@wuespace/telestion-client-types';
import { ConfigControls } from './config';
import { WidgetProps } from './model';
import { Widget as WidgetRenderer } from './widget';

export const widget: Widget<WidgetProps> = {
name: 'myFirstWidget',
title: 'my-first-widget',
version: '0.0.0',
Widget: WidgetRenderer,
ConfigControls
};

Step 1: Define the configuration options

First, you need to define a type for your configuration options.

Create a new file called model.ts, and export an interface WidgetProps with the corresponding type:

src/widgets/my-first-widget/model.ts
import { GenericProps } from '@wuespace/telestion-client-types';

/**
* Props for the "my-first-widget" widget
*/
export interface WidgetProps extends GenericProps {
/**
* Whether the widget should be read-only. If `true`, the reset buttons get disabled.
*/
isReadonly: boolean;
}

Update your widget's index.ts to define your new interface as the widget's configuration type:

src/widgets/my-first-widget/index.ts
import { Widget } from '@wuespace/telestion-client-types';
import { WidgetProps } from './model';
import { Widget as WidgetRenderer } from './widget';

export const widget: Widget<WidgetProps> = {
name: 'myFirstWidget',
title: 'my-first-widget',
version: '0.0.0',
Widget: WidgetRenderer
};
Type error in src/widgets/index.ts depending on tc-cli version

Depending on the version of tc-cli you're using, this might lead to a type error in your src/widgets/index.ts file.

If that's the case, append as Widget to your widget's declaration:

src/widgets/index.ts
import { Widget } from '@wuespace/telestion-client-types';
// [...]
import { widget as myFirstWidget } from './my-first-widget';
// IMPORT_INSERT_MARK

export const projectWidgets: Widget[] = [
// ARRAY_FIRST_ELEMENT_INSERT_MARK
myFirstWidget as Widget,
// [...]
sampleWidget
];

Step 2: Adjust the widget

The props passed to your widget now have the WidgetProps type.

Use the props' isReadonly property to deactivate the action buttons if the property's value is true:

src/widgets/my-first-widget/widget.tsx
// [...]
import { WidgetProps } from './model';

export function Widget(props: WidgetProps) {
return (
<View padding={'size-200'} height={'100%'}>
<Flex direction="column" width="100%">
<Heading level={3}>Connection Status</Heading>
<Divider size="M" marginBottom={'size-200'} />
<Indicator system="SAT A" isReadonly={props.isReadonly} />
<Indicator system="SAT B" isReadonly={props.isReadonly} />
<Indicator system="SAT C" isReadonly={props.isReadonly} />
</Flex>
</View>
);
}

function Indicator(props: { system: string; isReadonly: boolean }) {
const broadcast = useBroadcast('reset');
const status = useChannelLatest(`system-status/${props.system}`) ?? false;

return (
<Flex alignItems={'baseline'} gap={'size-200'}>
<StatusLight variant={status ? 'positive' : 'negative'}>
{props.system} {status ? 'Connected' : 'Disconnected'}
</StatusLight>
<ActionButton
onPress={() => broadcast({ system: props.system })}
isDisabled={props.isReadonly}
>
Reset
</ActionButton>
</Flex>
);
}

Step 3: Add default options to the user configuration

To make sure that the widget always gets the correct type, you need to specify the widget usage's default configuration options in your dashboard's declaration.

Add the initialProps field with isReadonly set to false to your widget's declaration in your sample-user-config.ts:

src/model/sample-user-config.ts
// [...]
{
id: '1',
widgetName: 'myFirstWidget',
width: 2,
height: 2,
initialProps: {
isReadonly: false
}
},
// [...]

Step 4: Add the configuration control interface component

The telestion-client library makes it easy to not only make your widgets configurable through the dashboard declaration, but also let your users adjust a widget's configuration.

You don't have to worry about how to make this accessible to the user, all you need to do is to define a user interface for adjusting the configuration options and declare it.

Create a file config.tsx in your widget's folder and define a ConfigControls component like this:

src/widgets/my-first-widget/config.tsx
import { Checkbox, View } from '@adobe/react-spectrum';
import { BaseConfigControlsProps } from '@wuespace/telestion-client-types';
import { WidgetProps } from './model';

export function ConfigControls(props: BaseConfigControlsProps<WidgetProps>) {
return (
<View padding="size-200">
<Checkbox
isSelected={props.currentProps.isReadonly}
onChange={isReadonly => props.onUpdate({ isReadonly })}
>
Readonly (disable buttons)
</Checkbox>
</View>
);
}

The props contain two properties:

  • currentProps, which is of your own WidgetProps type, and represents the current configuration
  • onUpdate(partial: Partial<WidgetProps>), which you can use to set the user configuration (comparable to the useState() setter function)
Partial state in onUpdate

The onUpdate function merges the partial state you pass into it with your current state.

For example, if currentProps is { a: 1, c: 3 } and you call onUpdate({ b: 2, c: 4 }), the resulting currentProps are { a: 1, b: 2, c: 4 }.

All you need to do now to expose these configuration controls to your users is to declare your component in the widget's index.ts:

src/widgets/my-first-widget/index.ts
import { Widget } from '@wuespace/telestion-client-types';
import { ConfigControls } from './config';
import { WidgetProps } from './model';
import { Widget as WidgetRenderer } from './widget';

export const widget: Widget<WidgetProps> = {
name: 'myFirstWidget',
title: 'my-first-widget',
version: '0.0.0',
Widget: WidgetRenderer,
ConfigControls
};

With that, your users can now open your widget's configuration page through the widget's context menu (right mouse button):

Link to

Next steps

You now know all the basics for building Telestion Client Widgets. But there are lots of other features the telestion-client APIs allow you to do. If you want, you can just keep reading through the tutorials.