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

docs(widgets) Custom Widget Developer Guide #9304

Open
wants to merge 23 commits into
base: master
Choose a base branch
from

Conversation

chrisgervang
Copy link
Collaborator

For #7946

Background

Adding developer guide to include more advanced examples about how to write both react and non-react widgets. This way our getting-started examples can be very simple, and we can have something to point more advanced users to when they're writing their own widgets.

Change List

  • docs

@chrisgervang chrisgervang changed the title docs(widgets) Widget Developer Guide docs(widgets) Custom Widget Developer Guide Dec 18, 2024
@chrisgervang chrisgervang requested a review from ibgreen December 18, 2024 05:25
@coveralls
Copy link

coveralls commented Dec 18, 2024

Coverage Status

coverage: 91.675% (-0.03%) from 91.706%
when pulling 1392345 on chr/widget-dev-guide
into e3533e0 on master.

Copy link
Collaborator

@felixpalmer felixpalmer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job ❤️

docs/developer-guide/custom-widgets/README.md Outdated Show resolved Hide resolved
{layers.filter(layer => (
this.deck?.props.layerFilter({layer, viewport})
)).map((layer) => {
<li key={layer.id}>{layer.id}</li>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be much more code to show how control the layers in some way? For example, have a checkbox next to each layer to toggle visibility in that view?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can try it.. but controlling anything the user sets starts to get into a territory where user state and internal state can get out of sync. At least, there's nothing preventing user-space from just overriding any changes a widget makes to a layer. And there's some risk of introducing a race condition.

I think what this would look like with our current API is something like:

this.deck?.setProps({ layers: layers.map(layer => layer.clone({ visible: isVisible }) })

This could cause events to fire in the app that in-turn call setProps. Is there a way to draw the layers without alerting the user? Perhaps:

this.deck?._drawLayers('layer-list-widget', { layers: ... })

Copy link
Collaborator

@ibgreen ibgreen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome work.

FWIW - One thing that I suspect will make widgets critical for me over the next year is the ability to use them in Python/pydeck. It could be a next step, making these available in python.

docs/developer-guide/custom-widgets/README.md Outdated Show resolved Hide resolved
docs/developer-guide/custom-widgets/README.md Outdated Show resolved Hide resolved
/**
* Widget positioning within the view. Default: 'top-left'.
*/
placement?: WidgetPlacement;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider exporting a set of base WidgetProps so that widget writers don't need to retype all of that?

Suggested change
placement?: WidgetPlacement;
export type WidgetProps = {
id?: string;
placement?: WidgetPlacement;
viewId?: string | null;
};
...
export type AwesomeWidgetProps = WidgetProps & {
customText: string;
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea to encourage consistency, though it's still 100% up to the widget authors to decide how they implement this since we're only defining an interface.

docs/developer-guide/custom-widgets/README.md Outdated Show resolved Hide resolved
}
```

## Best Practices
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section looks a little "lost" here at the very end of the page. Maybe lead with something like this before all the examples?. Or maybe the section will grow and it will look more natural.

docs/developer-guide/custom-widgets/universal-widgets.md Outdated Show resolved Hide resolved

## Handling View Interaction Events

A widget can update in response to a user interacting with the deck.gl view the widget is attached to.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is easily the most important section and needs its own page IMHO. (I do understand that we have work to do here.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Say more? What kind of example would you like to see?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been struggling to port the graph layers controller React component to a widget, I was looking for docs on how to implement zooms and pans

https://github.com/visgl/deck.gl-community/blob/master/modules/graph-layers/src/widgets/view-control-widget.tsx#L325

docs/developer-guide/custom-widgets/universal-widgets.md Outdated Show resolved Hide resolved

Below is a comprehensive example demonstrating a layer list widget implemented using Preact for dynamic UI rendering:

```tsx
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should really use MDX capabilities and make these widgets live on the pages...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Pessimistress suggested I can embed CodePen (e.g. LightingEffect) and found a react template too https://codepen.io/AdventureBear/pen/LYqwbwx

}
```

##### Applying the deck.gl widget design system
Copy link
Collaborator Author

@chrisgervang chrisgervang Dec 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should applying the design system be it's own page?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, the styling system is an important (top-level) part of your design!

@chrisgervang chrisgervang mentioned this pull request Dec 19, 2024
44 tasks
Copy link
Collaborator

@ibgreen ibgreen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is getting quite good.

I believe that a couple of iterative passes to make this even more logical and incremental to readers and would improve it further.

I added some suggestions to show what I mean.

Define a prop for adding a CSS class

```ts
interface AwesomeWidgetProps {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Total nit: Maybe tone it down somewhat and call it CustomWidget? "Awesome" seems a bit out of style with rest of the docs.

@@ -0,0 +1,77 @@
# Writing Your Own Widget

## Preparations
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
## Preparations
## Preliminaries


* **[Implement a universal widget](./universal-widgets.md)** - A "universal widget" is a widget compatible with any deck.gl application and is UI framework agnostic. This is the best option for developing widgets intended to work across the deck.gl ecosystem.
* **[Use Preact in a universal widget](./preact-widgets.md)** - Preact is a lightweight virtual DOM library commonly used to implement dynamic widget UI. It enables you to create highly interactive widgets without tightly coupling their internals to an application’s UI framework.
* **[Wrap widgets in a React component](./react-widgets.md)** - If you are developing a custom Widget for a React application, you can use React to build the UI. This approach allows you to use React components and can coexist alongside other widgets.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These bullets do not fully explain the situation the way I see it.

It seems to me that there are two types of widgets - "universal" and "react".

Suggested change
* **[Wrap widgets in a React component](./react-widgets.md)** - If you are developing a custom Widget for a React application, you can use React to build the UI. This approach allows you to use React components and can coexist alongside other widgets.
- universal widgets can be 1) used in React apps 2) wrapped into React widgets, but the reverse is not true.
For React specifically, you have several options:
- You can use the universal widgets by supplying them to the `<DeckGL widgets={[new Widget()]} />` prop.
- You can wrap universal components into React components using the `useWidget()` hook. This way you can specify your components using JSX syntax..
- If you are creating new components, can also create your own React components that implement the DeckGL widget interface. The advantage is that you write your code in the same style as the rest of your React application, however, these components would not be reusable in non-React applications.```

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Even calling it "react" doesn't really say what this is.. it's fine, but people could use other frameworks, in theory.

Really, they're making the choice between implementing UI within the widget class, or outside of it.

Do any of these terms pair better? "universal", "internal", or "portable" for UI within the widget.

"react", "external", or "BYOF" (bring your own framework) for UI injected into the widget class.

* **[Use Preact in a universal widget](./preact-widgets.md)** - Preact is a lightweight virtual DOM library commonly used to implement dynamic widget UI. It enables you to create highly interactive widgets without tightly coupling their internals to an application’s UI framework.
* **[Wrap widgets in a React component](./react-widgets.md)** - If you are developing a custom Widget for a React application, you can use React to build the UI. This approach allows you to use React components and can coexist alongside other widgets.

## Creating The Widget class
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
## Creating The Widget class
## Creating a new Widget

docs/developer-guide/custom-widgets/preact-widgets.md Outdated Show resolved Hide resolved
@@ -0,0 +1,215 @@
# Wrapping Widgets with React Components
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Wrapping Widgets with React Components
# React Widgets
## Wrapping Widgets with React Components

}
```

##### Applying the deck.gl widget design system
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, the styling system is an important (top-level) part of your design!


Widgets in deck.gl allow developers to create custom UI elements that are deeply integrated into the deck.gl rendering system. This guide covers the steps to implement widgets that are framework-agnostic, ensuring compatibility across various applications.

## Implementing the Widget Lifecycle Functions
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be worth having a separate first page describing the widget lifecycle and interface before listing all the sundry ways of implementing this lifecycle/interface.

@@ -0,0 +1,374 @@
# Universal Widgets

Widgets in deck.gl allow developers to create custom UI elements that are deeply integrated into the deck.gl rendering system. This guide covers the steps to implement widgets that are framework-agnostic, ensuring compatibility across various applications.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move the first sentence into an introductory page (about-widgets.md).

Suggested change
Widgets in deck.gl allow developers to create custom UI elements that are deeply integrated into the deck.gl rendering system. This guide covers the steps to implement widgets that are framework-agnostic, ensuring compatibility across various applications.
This guide covers the steps to implement widgets that are "universal" or framework-agnostic, ensuring compatibility across various applications.
(Widgets in deck.gl allow developers to create custom UI elements that are deeply integrated into the deck.gl rendering system. )


### Adding a Widget

[`onAdd({deck, viewId}): HTMLElement?`](../../api-reference/core/widget.md#onadd) - This method provides deck.gl with the root DOM element of your widget. This element is positioned based on `placement` and `viewId` members.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lifecycle method, Deck.gl calls this method at some point?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's called when the widget is first added.

Co-authored-by: Ib Green <7025232+ibgreen@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants