Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 144 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,144 @@
# Widgetized dashboard
# Widgetized Dashboard

A generic, reusable PatternFly component library providing a customizable widget-based dashboard with drag-and-drop functionality. This library was created by lifting and adapting code from the [RedHatInsights/widget-layout](https://github.com/RedHatInsights/widget-layout) repository, removing console-specific dependencies to make it suitable for any PatternFly application.

## Features

- **Drag-and-Drop Grid Layout**: Powered by `react-grid-layout` with responsive breakpoints
- **Widget Drawer**: Easy widget selection and management
- **Lock/Unlock Widgets**: Prevent accidental changes to widget positions and sizes
- **Resize Widgets**: Adjust widget dimensions with corner handles
- **Responsive Design**: Automatic layout adjustments for xl, lg, md, and sm breakpoints
- **Customizable**: Fully configurable widgets with custom icons, titles, and content
- **TypeScript Support**: Full type definitions included
- **No External Dependencies**: Self-contained state management (no Jotai, Redux, or other state libraries required)

## Installation

```bash
yarn add @patternfly/widgetized-dashboard
```

Or with npm:

```bash
npm install @patternfly/widgetized-dashboard
```

### Peer Dependencies

Make sure you have the required peer dependencies installed:

```bash
yarn add react react-dom react-router-dom @patternfly/react-core @patternfly/react-icons
```

## Quick Start

```tsx
import React from 'react';
import { WidgetLayout, WidgetMapping, ExtendedTemplateConfig } from '@patternfly/widgetized-dashboard';
import { CubeIcon } from '@patternfly/react-icons';

// Define your widgets
const widgetMapping: WidgetMapping = {
'example-widget': {
defaults: { w: 2, h: 3, maxH: 6, minH: 2 },
config: {
title: 'Example Widget',
icon: <CubeIcon />
},
renderWidget: (id) => <div>Widget content goes here</div>
}
};

// Define initial layout (or load from API/localStorage)
const initialTemplate: ExtendedTemplateConfig = {
xl: [
{ i: 'example-widget#1', x: 0, y: 0, w: 2, h: 3, widgetType: 'example-widget', title: 'Example Widget' }
],
lg: [
{ i: 'example-widget#1', x: 0, y: 0, w: 2, h: 3, widgetType: 'example-widget', title: 'Example Widget' }
],
md: [
{ i: 'example-widget#1', x: 0, y: 0, w: 2, h: 3, widgetType: 'example-widget', title: 'Example Widget' }
],
sm: [
{ i: 'example-widget#1', x: 0, y: 0, w: 1, h: 3, widgetType: 'example-widget', title: 'Example Widget' }
]
};

function App() {
return (
<WidgetLayout
widgetMapping={widgetMapping}
initialTemplate={initialTemplate}
onTemplateChange={(template) => {
// Save template to API or localStorage
console.log('Template changed:', template);
}}
/>
);
}
```

## Documentation

- [Getting Started Guide](./packages/module/patternfly-docs/content/examples/basic.md)
- [API Reference](./packages/module/patternfly-docs/content/design-guidelines/design-guidelines.md)

## Key Components

### WidgetLayout

The main component that provides the complete dashboard experience with grid layout and widget drawer.

### GridLayout

The core layout engine with drag-and-drop functionality (can be used standalone).

### WidgetDrawer

The widget selection drawer (can be used standalone with GridLayout).

### GridTile

Individual widget tile wrapper with actions menu (used internally by GridLayout).

## Differences from widget-layout

This library is based on [RedHatInsights/widget-layout](https://github.com/RedHatInsights/widget-layout) but has been adapted to be a generic, reusable PatternFly component:

### Removed
- ❌ Scalprum federated module loading
- ❌ Chrome Services API calls for template persistence
- ❌ Console-specific authentication (useCurrentUser)
- ❌ Console-specific analytics (useChrome)
- ❌ Jotai state management atoms
- ❌ Console-specific icons and branding

### Added
- ✅ Generic widget rendering via `renderWidget` prop
- ✅ Prop-based template management (bring your own state management)
- ✅ Optional analytics callback
- ✅ Standalone component usage (no external state required)
- ✅ Full TypeScript support
- ✅ Simplified API without console dependencies

## Browser Support

Modern browsers (Chrome, Firefox, Safari, Edge) with ES6 support.

## License

MIT

## Contributing

### AI-assisted development guidelines

Please reference [PatternFly's AI-assisted development guidelines](https://github.com/patternfly/.github/blob/main/CONTRIBUTING.md) if you'd like to contribute code generated using AI.

## Credits

This library is based on the [RedHatInsights/widget-layout](https://github.com/RedHatInsights/widget-layout) repository, adapted to be a generic PatternFly component.
7 changes: 6 additions & 1 deletion packages/module/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@
"access": "public"
},
"dependencies": {
"@patternfly/react-core": "^6.3.1"
"@patternfly/react-core": "^6.3.1",
"@patternfly/react-icons": "^6.3.1",
"clsx": "^2.1.0",
"react-grid-layout": "^1.5.1"
},
"peerDependencies": {
"react": "^18",
Expand All @@ -46,7 +49,9 @@
"@patternfly/patternfly-a11y": "^5.1.0",
"@patternfly/react-code-editor": "^6.3.1",
"@patternfly/react-table": "^6.3.1",
"@types/react-grid-layout": "^1.3.5",
"monaco-editor": "^0.53.0",
"nodemon": "^3.0.0",
"react-monaco-editor": "^0.59.0",
"rimraf": "^6.0.1"
}
Expand Down

This file was deleted.

4 changes: 0 additions & 4 deletions packages/module/patternfly-docs/content/examples/Basic.tsx

This file was deleted.

146 changes: 146 additions & 0 deletions packages/module/patternfly-docs/content/examples/BasicExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import React from 'react';
import WidgetLayout from '../../../src/WidgetLayout/WidgetLayout';
import { WidgetMapping, ExtendedTemplateConfig } from '../../../src/WidgetLayout/types';
import { CubeIcon, ChartLineIcon, BellIcon, ExternalLinkAltIcon, ArrowRightIcon } from '@patternfly/react-icons';
import { Card, CardBody, CardFooter, Content, Icon } from '@patternfly/react-core';

interface SimpleWidgetProps {
id: number;
body: string;
linkTitle: string;
url: string;
isExternal?: boolean;
}

const CardExample: React.FunctionComponent<SimpleWidgetProps> = (props) => (
<Card isPlain>
<CardBody className="pf-v6-u-p-md pf-v6-u-pb-0">
<Content
key={props.id}
className="pf-v6-u-display-flex pf-v6-u-flex-direction-column"
>
<Content component="p" className="pf-v6-u-flex-grow-1">
{props.body}
</Content>
</Content>
</CardBody>
<CardFooter className="pf-v6-u-p-md">
{props.isExternal ? (
<a href={props.url}>
{props.linkTitle}
<Icon className="pf-v6-u-ml-sm" isInline>
<ExternalLinkAltIcon />
</Icon>
</a>
) : (
<a href={props.url}>
{props.linkTitle}
<Icon className="pf-v6-u-ml-sm" isInline>
<ArrowRightIcon />
</Icon>
</a>
)}
</CardFooter>
</Card>
);

// Example widget content components
const ExampleWidget1 = () => (
<CardExample
id={1}
body="This is the content of the first example widget. You can put any React content here!"
linkTitle="View details"
url="https://www.patternfly.org"
isExternal={true}
/>
);

const ExampleWidget2 = () => (
<CardExample
id={2}
body="Chart and visualization content would be displayed here."
linkTitle="View full report"
url="#"
isExternal={false}
/>
);

const ExampleWidget3 = () => (
<CardExample
id={3}
body="Recent notifications and updates will appear in this widget."
linkTitle="View all notifications"
url="#"
isExternal={false}
/>
);

// Define widget mapping
const widgetMapping: WidgetMapping = {
'example-widget-1': {
defaults: { w: 2, h: 3, maxH: 6, minH: 2 },
config: {
title: 'Example Widget',
icon: <CubeIcon />,
headerLink: {
title: 'View details',
href: '#'
}
},
renderWidget: () => <ExampleWidget1 />
},
'chart-widget': {
defaults: { w: 2, h: 4, maxH: 8, minH: 3 },
config: {
title: 'Chart Widget',
icon: <ChartLineIcon />
},
renderWidget: () => <ExampleWidget2 />
},
'notifications-widget': {
defaults: { w: 1, h: 3, maxH: 6, minH: 2 },
config: {
title: 'Notification Widget',
icon: <BellIcon />
},
renderWidget: () => <ExampleWidget3 />
}
};

// Define initial template
const initialTemplate: ExtendedTemplateConfig = {
xl: [
{ i: 'example-widget-1#1', x: 0, y: 0, w: 2, h: 3, widgetType: 'example-widget-1', title: 'Example Widget' },
{ i: 'chart-widget#1', x: 2, y: 0, w: 2, h: 4, widgetType: 'chart-widget', title: 'Chart Widget' }
],
lg: [
{ i: 'example-widget-1#1', x: 0, y: 0, w: 2, h: 3, widgetType: 'example-widget-1', title: 'Example Widget' },
{ i: 'chart-widget#1', x: 0, y: 3, w: 2, h: 4, widgetType: 'chart-widget', title: 'Chart Widget' }
],
md: [
{ i: 'example-widget-1#1', x: 0, y: 0, w: 2, h: 3, widgetType: 'example-widget-1', title: 'Example Widget' },
{ i: 'chart-widget#1', x: 0, y: 3, w: 2, h: 4, widgetType: 'chart-widget', title: 'Chart Widget' }
],
sm: [
{ i: 'example-widget-1#1', x: 0, y: 0, w: 1, h: 3, widgetType: 'example-widget-1', title: 'Example Widget' },
{ i: 'chart-widget#1', x: 0, y: 3, w: 1, h: 4, widgetType: 'chart-widget', title: 'Chart Widget' }
]
};

export const BasicExample: React.FunctionComponent = () => {
const [template, setTemplate] = React.useState(initialTemplate);

return (
<div style={{ height: '600px', width: '100%', overflow: 'auto', position: 'relative', isolation: 'isolate' }}>
<WidgetLayout
widgetMapping={widgetMapping}
initialTemplate={template}
onTemplateChange={(newTemplate) => {
setTemplate(newTemplate);
}}
documentationLink="https://www.patternfly.org"
initialDrawerOpen={true}
/>
</div>
);
};
Loading
Loading