Skip to content

Commit 3da76ce

Browse files
committedDec 14, 2021
👻 A simple example widget
A simple example which you can use as a template for creating your own widget. Takes two optional parameters (text and count), and fetches a list of images from dummyapis.com, and renders the results to the UI.
1 parent 6df95c9 commit 3da76ce

File tree

4 files changed

+152
-0
lines changed

4 files changed

+152
-0
lines changed
 

‎docs/widgets.md

+25
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a
1616
- [Stock Price History](#stock-price-history)
1717
- [Joke of the Day](#joke)
1818
- [Flight Data](#flight-data)
19+
- [Example Widget](#example-widget)
1920
- [Self-Hosted Services Widgets](#dynamic-widgets)
2021
- [Dynamic Widgets](#dynamic-widgets)
2122
- [Iframe Widget](#iframe-widget)
@@ -366,6 +367,30 @@ Displays airport departure and arrival flights, using data from [AeroDataBox](ht
366367

367368
---
368369

370+
### Example Widget
371+
372+
A simple example widget, to use as a template. Fetches and displays a list of images, from [Dummy APIs](https://dummyapis.com/).
373+
374+
<p align="center"><img width="400" src="https://i.ibb.co/VSPn84t/example-widget.png" /></p>
375+
376+
##### Options
377+
378+
**Field** | **Type** | **Required** | **Description**
379+
--- | --- | --- | ---
380+
**`text`** | `string` | _Optional_ | Text to display in the images. Defaults to `Dashy`
381+
**`count`** | `number` | _Optional_ | The number of images to be rendered. Defaults to `5`
382+
383+
##### Example
384+
385+
```yaml
386+
- type: example
387+
options:
388+
text: Hello
389+
count: 3
390+
```
391+
392+
---
393+
369394
## Dynamic Widgets
370395

371396
### Iframe Widget
+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<template>
2+
<div class="example-wrapper">
3+
<template v-if="images">
4+
<div v-for="(image, index) in images" :key="index" class="image-row">
5+
<p class="picture-title">{{ image.title }}</p>
6+
<img class="picture-result" :src="image.path"/>
7+
</div>
8+
</template>
9+
</div>
10+
</template>
11+
12+
<script>
13+
/**
14+
* A simple example which you can use as a template for creating your own widget.
15+
* Takes two optional parameters (`text` and `count`), and fetches a list of images
16+
* from dummyapis.com, then renders the results to the UI.
17+
*/
18+
19+
import axios from 'axios';
20+
import WidgetMixin from '@/mixins/WidgetMixin';
21+
import { widgetApiEndpoints } from '@/utils/defaults';
22+
23+
export default {
24+
mixins: [WidgetMixin],
25+
components: {},
26+
data() {
27+
return {
28+
images: null, // Will store our results from the API
29+
};
30+
},
31+
mounted() {
32+
this.fetchData();
33+
},
34+
computed: {
35+
/* Get the users chosen number of results, from this.options.count
36+
* If not present, or not a number, then return the default (5)
37+
*/
38+
count() {
39+
const usersChoice = this.options.count;
40+
if (!usersChoice || !Number.isNaN(usersChoice)) {
41+
return 5;
42+
}
43+
return usersChoice;
44+
},
45+
/* Get users desired image text, or return `Dashy` */
46+
text() {
47+
const usersChoice = this.options.text;
48+
if (!usersChoice) return 'Dashy';
49+
return usersChoice;
50+
},
51+
/* Generate the data endpoint for the API request */
52+
endpoint() {
53+
return `${widgetApiEndpoints.exampleEndpoint}?text=${this.text}&noofimages=${this.count}`;
54+
},
55+
},
56+
methods: {
57+
/* The update() method extends mixin, used to update the data.
58+
* It's called by parent component, when the user presses update
59+
*/
60+
update() {
61+
this.startLoading();
62+
this.fetchData();
63+
},
64+
/* Make the data request to the computed API endpoint */
65+
fetchData() {
66+
axios.get(this.endpoint)
67+
.then((response) => {
68+
// The request has completed successfully, call function to process the data
69+
this.processData(response.data);
70+
})
71+
.catch((dataFetchError) => {
72+
// If an error occurs, then calling this.error() will handle this gracefully
73+
this.error('Unable to fetch data', dataFetchError);
74+
})
75+
.finally(() => {
76+
// When the request is done, hide the loader
77+
this.finishLoading();
78+
});
79+
},
80+
/* Convert API response data into a format to be consumed by the UI */
81+
processData(response) {
82+
const results = [];
83+
response.forEach((image, index) => {
84+
results.push({
85+
path: image,
86+
title: `Image ${index + 1}`,
87+
});
88+
});
89+
// Now, in the HTML, we can reference the `images` array
90+
this.images = results;
91+
},
92+
},
93+
};
94+
</script>
95+
96+
<style scoped lang="scss">
97+
.example-wrapper {
98+
.image-row {
99+
display: flex;
100+
align-items: center;
101+
justify-content: space-around;
102+
p.picture-title {
103+
font-size: 1.2rem;
104+
color: var(--widget-text-color);
105+
}
106+
img.picture-result {
107+
width: 4rem;
108+
margin: 0.5rem 0;
109+
border-radius: var(--curve-factor);
110+
}
111+
&:not(:last-child) {
112+
border-bottom: 1px dashed var(--widget-text-color);
113+
}
114+
}
115+
}
116+
117+
</style>

‎src/components/Widgets/WidgetBase.vue

+9
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@
116116
@error="handleError"
117117
:ref="widgetRef"
118118
/>
119+
<ExampleWidget
120+
v-else-if="widgetType === 'example'"
121+
:options="widgetOptions"
122+
@loading="setLoaderState"
123+
@error="handleError"
124+
:ref="widgetRef"
125+
/>
119126
<!-- No widget type specified -->
120127
<div v-else>{{ handleError('No widget type was specified') }}</div>
121128
</div>
@@ -145,6 +152,7 @@ import Jokes from '@/components/Widgets/Jokes.vue';
145152
import Flights from '@/components/Widgets/Flights.vue';
146153
import IframeWidget from '@/components/Widgets/IframeWidget.vue';
147154
import EmbedWidget from '@/components/Widgets/EmbedWidget.vue';
155+
import ExampleWidget from '@/components/Widgets/ExampleWidget.vue';
148156
149157
export default {
150158
name: 'Widget',
@@ -167,6 +175,7 @@ export default {
167175
Flights,
168176
IframeWidget,
169177
EmbedWidget,
178+
ExampleWidget,
170179
},
171180
props: {
172181
widget: Object,

‎src/utils/defaults.js

+1
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ module.exports = {
216216
jokes: 'https://v2.jokeapi.dev/joke/',
217217
flights: 'https://aerodatabox.p.rapidapi.com/flights/airports/icao/',
218218
rssToJson: 'https://api.rss2json.com/v1/api.json',
219+
exampleEndpoint: 'https://hub.dummyapis.com/ImagesList',
219220
},
220221
/* URLs for web search engines */
221222
searchEngineUrls: {

0 commit comments

Comments
 (0)
Please sign in to comment.