Compare commits

..

1 Commits

Author SHA1 Message Date
Luccas Mateus
0f7d28e02e [flatuitable][m] - add bytes + parsingConfig 2023-09-20 08:12:32 -03:00
235 changed files with 16680 additions and 11920 deletions

View File

@ -0,0 +1,5 @@
---
'@portaljs/components': minor
---
FlatUiTables now accepts a bytes param and a parsingConfig param for CSV links

8
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,8 @@
{
"recommendations": [
"nrwl.angular-console",
"esbenp.prettier-vscode",
"firsttris.vscode-jest-runner",
"dbaeumer.vscode-eslint"
]
}

View File

@ -4,7 +4,7 @@ title: Developer docs for contributors
## Our repository
https://github.com/datopian/datahub
https://github.com/datopian/portaljs
Structure:
@ -17,7 +17,7 @@ Structure:
## How to contribute
You can start by checking our [issues board](https://github.com/datopian/datahub/issues).
You can start by checking our [issues board](https://github.com/datopian/portaljs/issues).
If you'd like to work on one of the issues you can:
@ -35,7 +35,7 @@ If you'd like to work on one of the issues you can:
If you have an idea for improvement, and it doesn't have a corresponding issue yet, simply submit a new one.
> [!note]
> Join our [Discord channel](https://discord.gg/KZSf3FG4EZ) do discuss existing issues and to ask for help.
> Join our [Discord channel](https://discord.gg/rTxfCutu) do discuss existing issues and to ask for help.
## Nx

View File

@ -1,51 +1,31 @@
<p align="center">
Bugs, issues and suggestions re PortalJS framework
<br />
<br /><a href="https://discord.gg/xfFDMPU9dC"><img src="https://dcbadge.vercel.app/api/server/xfFDMPU9dC" /></a>
</p>
<h1 align="center">
🌀 Portal.JS
<br />
Rapidly build rich data portals using a modern frontend framework
</h1>
## PortalJS framework
* [What is Portal.JS ?](#What-is-Portal.JS)
* [Features](#Features)
* [For developers](#For-developers)
* [Docs](#Docs)
* [Community](#Community)
* [Appendix](#Appendix)
* [What happened to Recline?](#What-happened-to-Recline?)
This repo and issue tracker are for
# What is Portal.JS
- PortalJS 🌀 - https://www.portaljs.com/
- DataHub Cloud ☁️ - https://datahub.io/
🌀 Portal.JS is a framework for rapidly building rich data portal frontends using a modern frontend approach. Portal.JS can be used to present a single dataset or build a full-scale data catalog/portal.
### Issues
Built in JavaScript and React on top of the popular [Next.js](https://nextjs.com/) framework. Portal.JS assumes a "decoupled" approach where the frontend is a separate service from the backend and interacts with backend(s) via an API. It can be used with any backend and has out of the box support for [CKAN](https://ckan.org/).
Found a bug: 👉 https://github.com/datopian/portaljs/issues/new
### Discussions
Got a suggestion, a question, want some support or just want to shoot the breeze 🙂
Head to the discussion forum: 👉 https://github.com/datopian/portaljs/discussions
### Chat on Discord
If you would prefer to get help via live chat check out our discord 👉
[Discord](https://discord.gg/xfFDMPU9dC)
### Docs
- For PortalJS go to https://www.portaljs.com/opensource
- For DataHub Cloud https://datahub.io/docs
## PortalJS Cloud 🌀
PortalJS Cloud 🌀 is a platform for rapidly creating rich data portal and publishing systems using a modern frontend approach. PortalJS Cloud can be used to publish a single dataset or build a full-scale data catalog/portal.
PortalJS Cloud is built in JavaScript and React on top of the popular [Next.js](https://nextjs.org) framework. PortalJS Cloud assumes a "decoupled" approach where the frontend is a separate service from the backend and interacts with backend(s) via an API. It can be used with any backend and has out of the box support for [CKAN](https://ckan.org/), GitHub, Frictionless Data Packages and more.
### Features
## Features
- 🗺️ Unified sites: present data and content in one seamless site, pulling datasets from a DMS (e.g. CKAN) and content from a CMS (e.g. Wordpress) with a common internal API.
- 👩‍💻 Developer friendly: built with familiar frontend tech (JavaScript, React, Next.js).
- 🔋 Batteries included: full set of portal components out of the box e.g. catalog search, dataset showcase, blog, etc.
- 🎨 Easy to theme and customize: installable themes, use standard CSS and React+CSS tooling. Add new routes quickly.
- 🧱 Extensible: quickly extend and develop/import your own React components
- 📝 Well documented: full set of documentation plus the documentation of Next.js.
- 📝 Well documented: full set of documentation plus the documentation of Next.js and Apollo.
### For developers
@ -53,3 +33,25 @@ PortalJS Cloud is built in JavaScript and React on top of the popular [Next.js](
- 🚀 Next.js framework: so everything in Next.js for free: Server Side Rendering, Static Site Generation, huge number of examples and integrations, etc.
- Server Side Rendering (SSR) => Unlimited number of pages, SEO and more whilst still using React.
- Static Site Generation (SSG) => Ultra-simple deployment, great performance, great lighthouse scores and more (good for small sites)
#### **Check out the [Portal.JS website](https://portaljs.org/) for a gallery of live portals**
___
# Docs
Access the Portal.JS documentation at:
https://portaljs.org/docs
- [Examples](https://portaljs.org/docs#examples)
# Community
If you have questions about anything related to Portal.JS, you're always welcome to ask our community on [GitHub Discussions](https://github.com/datopian/portal.js/discussions) or on our [Discord server](https://discord.gg/EeyfGrGu4U).
# Appendix
## What happened to Recline?
Portal.JS used to be Recline(JS). If you are looking for the old Recline codebase it still exists: see the [`recline` branch](https://github.com/datopian/portal.js/tree/recline). If you want context for the rename see [this issue](https://github.com/datopian/portal.js/issues/520).

View File

@ -2,7 +2,7 @@
**🚩 UPDATE April 2023: This example is now deprecated - though still works!. Please use the [new CKAN examples](https://github.com/datopian/portaljs/tree/main/examples)**
This example shows how you can build a full data portal using a CKAN Backend with a Next.JS Frontend powered by Apollo, a full fledged guide is available as a [blog post](https://portaljs.com/blog/example-ckan-2021)
This example shows how you can build a full data portal using a CKAN Backend with a Next.JS Frontend powered by Apollo, a full fledged guide is available as a [blog post](https://portaljs.org/blog/example-ckan-2021)
## Developers

View File

@ -1,7 +1,7 @@
This is a repo intended to serve as an example of a data catalog that get its data from a CKAN Instance.
```
npx create-next-app <app-name> --example https://github.com/datopian/datahub/tree/main/examples/ckan-ssg
npx create-next-app <app-name> --example https://github.com/datopian/portaljs/tree/main/examples/ckan-example
cd <app-name>
```
@ -19,7 +19,7 @@ npm run dev
Congratulations, you now have something similar to this running on `http://localhost:4200`
![](https://media.discordapp.net/attachments/1069718983604977754/1098252297726865408/image.png?width=853&height=461)
If you go to any one of those pages by clicking on `More info` you will see something similar to this
If yo go to any one of those pages by clicking on `More info` you will see something similar to this
![](https://media.discordapp.net/attachments/1069718983604977754/1098252298074988595/image.png?width=853&height=461)
## Deployment

View File

@ -1,6 +1,6 @@
This example creates a portal/showcase for a single dataset. The dataset should be a [Frictionless dataset (data package)][fd] i.e. there should be a `datapackage.json`.
[fd]: https://specs.frictionlessdata.io/data-package/
[fd]: https://frictionlessdata.io/data-packages/
## How to use

View File

@ -1,9 +1,3 @@
# PortalJS Demo replicating the FiveThirtyEight data portal
## 👉 https://fivethirtyeight.portaljs.org 👈
Here's a blog post we wrote about it: https://www.datopian.com/blog/fivethirtyeight-replica
This is a replica of the awesome data.fivethirtyeight.com using PortalJS.
You might be asking why we did that, there are three main reasons:

View File

@ -59,7 +59,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
<div className="md:flex items-center gap-x-3 text-[#3c3c3c] -mb-1 hidden">
<a
className="hover:opacity-75 transition"
href="https://portaljs.com"
href="https://portaljs.org"
>
Built with 🌀PortalJS
</a>
@ -77,7 +77,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
<li>
<a
className="hover:opacity-75 transition"
href="https://portaljs.com"
href="https://portaljs.org"
>
PortalJS
</a>

View File

@ -6,7 +6,7 @@ A `datasets.json` file is used to specify which datasets are going to be part of
The application contains an index page, which lists all the datasets specified in the `datasets.json` file, and users can see more information about each dataset, such as the list of data files in it and the README, by clicking the "info" button on the list.
You can read more about it on the [Data catalog with data on GitHub](https://portaljs.com/docs/examples/github-backed-catalog) blog post.
You can read more about it on the [Data catalog with data on GitHub](https://portaljs.org/docs/examples/github-backed-catalog) blog post.
## Demo

View File

@ -40,7 +40,7 @@ export function Datasets({ projects }) {
<Link
target="_blank"
className="underline"
href="https://portaljs.com/"
href="https://portaljs.org/"
>
🌀 PortalJS
</Link>

View File

@ -1 +1 @@
PortalJS Learn Example - https://portaljs.com/docs
PortalJS Learn Example - https://portaljs.org/docs

View File

@ -6,7 +6,7 @@ A `datasets.json` file is used to specify which datasets are going to be part of
The application contains an index page, which lists all the datasets specified in the `datasets.json` file, and users can see more information about each dataset, such as the list of data files in it and the README, by clicking the "info" button on the list.
You can read more about it on the [Data catalog with data on GitHub](https://portaljs.com/docs/examples/github-backed-catalog) blog post.
You can read more about it on the [Data catalog with data on GitHub](https://portaljs.org/docs/examples/github-backed-catalog) blog post.
## Demo

View File

@ -57,10 +57,6 @@ export function Header() {
title: 'Contributing',
href: '/contributing',
},
{
title: 'Help',
href: '/help',
},
{
title: 'Resources',
href: '/resources',
@ -77,10 +73,6 @@ export function Header() {
title: 'Working Group On Open Spending Data',
href: '/resources/wg/',
},
{
title: 'UK Departamental Spending',
href: '/resources/gb-spending',
},
],
},
];

View File

@ -17,7 +17,7 @@ export default function Footer() {
</a>
</div>
<div className="flex gap-x-2 items-center mx-auto h-20">
<p className="mt-8 text-base text-slate-500 md:mt-0">Built with <a href="https://portaljs.com" target="_blank" className='text-xl font-medium'>🌀 PortalJS</a></p>
<p className="mt-8 text-base text-slate-500 md:mt-0">Built with <a href="https://portaljs.org" target="_blank" className='text-xl font-medium'>🌀 PortalJS</a></p>
</div>
</div>
</footer>

View File

@ -1,25 +0,0 @@
---
section: help
lead: true
title: The OpenSpending Guide
authors:
- Neil Ashton
---
The OpenSpending Guide is the manual for OpenSpending, covering the entire process of finding, adding, and using data with OpenSpending in detail.
* Introduction
* [What is OpenSpending?](en/what-is-openspending)
* [What types of financial data can go into OpenSpending?](en/financial-data-types)
* [How does OpenSpending represent data?](en/data-model)
* Adding Data to OpenSpending
* [Overview](en/adding-data-overview)
* [Gathering data](en/gathering-data)
* [Formatting data](en/formatting-data)
* [Publishing data on the web](en/publishing-data)
* [Creating a dataset on OpenSpending](en/creating-dataset)
* [Modelling your data in OpenSpending](en/modelling-data)
* Visualizations
* [Create a visualization](en/create-viz)
* [Embed a visualization on your website](en/embed-viz)
**Up**: [OpenSpending guides](/help)

View File

@ -1,10 +0,0 @@
---
section: help
lead: true
title: Guía de OpenSpending (Gasto Abierto)
authors:
- Anders Pedersen
---
<a href="https://docs.google.com/a/okfn.org/document/d/1sl9Fvtbp5y-XDnY6cosZb5CJQw-oAD_zs1kVqBovk14/edit#heading=h.79vyyu8n203g">Guía de OpenSpending (Gasto Abierto)</a> (as a Google Doc).
The guide needs to be reformatted and posted here (in markdown). You can help by getting in touch with <a href="http://community.openspending.org/contribute/">the community here</a>.

View File

@ -1,27 +0,0 @@
---
section: help
lead: true
title: Ajouter des données dans OpenSpending
authors:
- lombardoelisabetta
---
Lune des contributions les plus utiles que vous pouvez faire sur OpenSpending est dajouter un nouveau jeu de données. Ce paragraphe vous explique en détails comment faire.
Une démarche classique pour importer un jeu de données sur OpenSpending suit les étapes suivantes:
* Rassembler les données lisibles par une machine depuis une source officielle.
* Convertir les données dans le format CSV supporté par OpenSpending. Nettoyer le jeu de données pour supprimer les erreurs et les éventuelles incohérences.
* Publier le jeu de données sur le web.
* Créer un jeu de données sur OpenSpending en indiquant la source (URL) du jeu de données.
* Créer un modèle en définissant le rôle de chaque colonne du jeu de données.
* Charger les données, ou corriger le jeu de données en vous aidant des recommandations fournies par OpenSpending sur la cohérence des données.
Chacune de ces étapes est détaillée dans les sections suivantes.
&nbsp;
**Suivant**:[<a href="../rassembler-les-donnees/">Rassembler les données</a>]
&nbsp;
**Précédent**: [<a href="../representation-des-donnees-dans-openspending/">Représentation des données dans OpenSpending</a>]

View File

@ -1,52 +0,0 @@
---
section: help
lead: true
title: Créer un jeu de données sur OpenSpending
authors:
- lombardoelisabetta
---
Pour pouvoir partager vos données sur OpenSpending, vous devez dabord vous enregistrer sur le site OpenSpending.org. Pour créer un jeu de données, il vous suffira ensuite dindiquer ses métadonnées et son URL.
###Créer un nouveau jeu de données
Connectez-vous sur OpenSpending avec vos informations dutilisateur. Vous serez ensuite redirigé vers le Dashboard. Cliquez sur le bouton **Import a Dataset** pour créer un nouveau jeu de données.
Lécran suivant vous invite à fournir les métadonnées qui définissent votre jeu de données. Voici les informations demandées :
* _Title_ (Titre): une description et un nom explicite pour le jeu de données.
* _Identifier_ (Identifiant): un titre court, utilisé dans lURL du jeu de données. Lidentifiant peut contenir uniquement des caractères alphanumériques, des tirets, et des traits bas ni espace blanc ni signe de ponctuation.
* _Category_ (Catégorie): pouvant être “Budget” (Budget), “Expenditure” (Dépense) ou “Other” (Autres). Référez-vous aux types de données financières pour des détails sur ces catégories.
* _Currency_ (devise): la devise utilisée pour les montants dans le jeu de données.
* _Countries_ (Pays): une liste de pays référencés dans le jeu de données. Le choix des pays est contraint à une liste de pays valides.
* _Languages_ (Langue): une liste des langues utilisées dans le jeu de données. Le choix des langues est contraint par une liste de langues valides.
* _Description_ (Description): une définition du jeu de données en langage simple.
Remplissez ces champs. Assurez-vous dinclure une description qui explique lorigine de votre jeu de données et les modifications apportées (par exemple que le jeu de données a été nettoyé).
Dès que toutes les métadonnées ont été renseignées, cliquez sur **Next Step** pour continuer.
###Ajouter une nouvelle source de données
Létape suivante vous emmène sur la page _Manage_ (Gestion). La page _Manage_ est utilisée pour ajouter la source du jeu de données. Elle est aussi utilisée pour créer le modèle de schéma de données qui permet à OpenSpending dinterpréter les informations. Cette étape, dénomée _modelling_ (modélisation) est expliquée en détail dans la section suivante du guide.
Pour ajouter une source de données à un jeu de données, cliquez sur **Add a source**. Une fenêtre apparaît vous demandant dentrer une URL. Indiquez lURL du fichier CSV que vous avez précédemment publié sur le web comme indiqué dans la section précédente du guide et cliquez sur _**Create**_. Vous verrez alors une fenêtre bleue indiquant quOpenSpending est en train dexaminer les données.
![Ajouter des données étape 1](http://blog.openspending.org/files/2013/08/image_2-e1375888360807.png)
Cliquez sur _**Refresh**_ ou utilisez le bouton de rafraîchissement de votre navigateur. Si OpenSpending parvient à analyser vos données, vous verrez alors un message vert vous disant que les données sont prêtes. Vous devriez également voir une liste correcte des colonnes de votre CSV.
![Ajouter des données étape 2](http://blog.openspending.org/files/2013/08/image_3-e1375888381459.png)
Notez que si vous fournissez à OpenSpending un fichier HTML à la place dun fichier CSV, lapplication analysera quand même le HTML comme si cétait un CSV. Le résultat ressemblera à cela:
![Ajouter des données étape 3](http://blog.openspending.org/files/2013/08/image_4-e1375888407751.png)
Si vous avez ajouté une mauvaise source, ce nest pas grave. Vous navez pas à utiliser cette source pour votre jeu de données final. OpenSpending demande un travail supplémentaire sur les données avant de pouvoir les publier. Ajoutez alors simplement la source correcte, et oubliez lautre source.
&nbsp;
**Suivant**:[<a href="../modeliser-vos-donnees-dans-openspending/">Modéliser vos données dans OpenSpending</a>]
&nbsp;
**Précédent**: [<a href="../publier-les-donnees-sur-le-web/">Publier les données sur le web</a>]

View File

@ -1,56 +0,0 @@
---
section: help
lead: true
title: Créer une visualisation
authors:
- lombardoelisabetta
---
La plateforme OpenSpending permet de créer facilement des visualisations de données. Trois visualisations sont proposées: le BubbleTree, la TreeMap et la Table des agrégats.
Toutes les modélisations dOpenSpending vous permettent de choisir une série de dimensions grâce auxquelles vous pouvez agréger vos données et les segmenter selon des propriétés de plus en plus précises. Chaque modélisation est créée de la même manière : en choisissant les dimensions à agréger et lordre dans lequel les segmenter.
Pour commencer à créer une modélisation, allez dans la page daccueil du jeu de données et sélectionnez _**Create a visualisation**_ dans le menu _Visualization_.
###BubbleTree
Le BubbleTree est une visualisation interactive qui présente les données de dépense aggrégée sous forme de bulles. Chaque bulle représente un (sous-)total aggrégé. La bulle centrale représente la somme aggrégée et les bulles qui lentourent représentent les autres sommes qui la composent. En cliquant sur nimporte quelle bulle, lutilisation voit la manière dont la somme se divise en dautres sous-totaux.
Pour créer un BubbleTree, choisissez les dimension dagrégation et lordre dans lequel vous voulez les agréger. Choisissez la première dimension dans la liste déroulante Level. Vous verrez alors le total agrégé de cette dimension sous la forme de la bulle centrale, avec les valeurs des dimensions qui lentourent et leurs propres totaux.
![Bumble Tree]({{ site.baseurl }}/img/blog/2013/08/image_14.png)
Pour ajouter un deuxième niveau, cliquez sur _**Add a level**_ et choisissez une nouvelle dimension. Les utilisateurs seront alors capables de cliquer sur les bulles pour les segmenter et voir comment les valeur du premier niveau se divisent dans les valeurs du second niveau.
![Bumble Tree]({{ site.baseurl }}/img/blog/2013/08/image_15.png)
###TreeMap
Le TreeMap représente les données de dépense agrégées sous forme de rectangle de couleur. Chaque rectangle représente un montant agrégé selon une dimension du jeu de données. Cliquer sur un rectangle permet dexplorer la répartition des sous-totaux suivant une autre dimension.
Pour créer un TreeMap, choisissez simplement les dimensions à agréger et leur niveau. En sélectionnant la dimension de premier niveau vous verrez apparaître la répartition des montants dans les rectangles suivant la dimension choisie.
![Tree Map]({{ site.baseurl }}/img/blog/2013/08/image_16.png)
Pour rendre la visualisation plus interactive, ajoutez des niveaux supplémentaires qui vous permettront dexplorer comment chaque montant se divise en sous-montants. Pour ajouter un second niveau, cliquez sur **Add a level** et choisissez une nouvelle dimension. Les utilisateurs peuvent désormais cliquer pour voir comment les montants se divisent.
![Visualisation interactive]({{ site.baseurl }}/img/blog/2013/08/image_17.png)
###Tableau dagrégats
Le tableau dagrégats est un simple tableau qui offre une vue des données numériques agrégées (additionnées) suivant la dimension choisie.
Sélectionnez une dimensions dans le menu _Columns_. Vous verrez alors apparâitre un tableau avec les montants et le pourcentage du montant total quils représentent. Par défaut, les lignes sont triées par pourcentage décroissant.
![Tableau dagrégats](http://blog.openspending.org/files/2013/08/image_18-e1375889043439.png)
En ajoutant une autre colonne en cliquant sur **Add a level**, le montant de la première colonne sera divisé en sous-totaux dans la seconde suivant la dimension sélectionnée. Notez que cette opération modifie les valeurs des pourcentages et donc lordre des lignes.
![Ajouter des colonnes](http://blog.openspending.org/files/2013/08/image_19-e1375889063736.png)
&nbsp;
**Suivant**:[<a href="../inclure-une-visualisation-sur-votre-site-web/">Inclure une visualisation sur votre site web</a>]
&nbsp;
**Précédent**: [<a href="../modeliser-vos-donnees-dans-openspending/">Modéliser vos données dans OpenSpending</a>]

View File

@ -1,48 +0,0 @@
---
section: help
lead: true
title: Formatter les données
authors:
- lombardoelisabetta
---
OpenSpending suppose que les données soient fournies dans un seul format simple.
###Fichier CSV
OpenSpending accepte les données dans un seul type de format de fichier, le format CSV (Comma-Separated Values). Un fichier CSV est un fichier texte qui représente les données sous la forme dun tableau, similaire à un format tableur. Dans un tableau, chaque entrée est représentée par une ligne, et chaque propriété de lentrée est représentée par une colonne. Dans un fichier CSV, les lignes sont subdivisées en colonnes à laide de virgules.
Les fichiers CSV acceptés par OpenSpending sont “dénormalisés”: cela signifie quils néconomisent pas despace en supprimant des valeurs redondantes. Ils doivent également être “rectangulaires”, cest-à-dire quils ont exactement le même nombre de colonnes pour chacune des lignes.
###Le format OpenSpending
Un fichier CSV doit avoir les caractéristiques suivantes pour OpenSpending.
1. Une ligne de titre. La première ligne du fichier CSV devrait contenir les noms des colonnes, séparés par des virgules.
2. Au moins 3 colonnes : un montant, une date (qui peut être juste une année), et le payeur ou le bénéficiaire (qui peut juste être le nom dun compte).
3. Des colonnes homogènes. Chaque colonne doit toujours représenter un seul type de valeur pour toutes les lignes (Il ne peut y avoir, par exemple, de sous-division).
4. Les lignes sont des points de données. Chaque ligne ne doit contenir quun type de données : une transaction ou une ligne budgétaire (elles ne peuvent représenter plusieurs transactions)
5. Aucune ligne ou cellule vide. Chaque ligne dun fichier devrait contenir toutes les informations requises afin de pouvoir construire lobjet résultant.
6. Pas de résultats aggrégés (exemple : sous total ou calculs à laide de formules). Ils seront calculés automatiquement par OpenSpending.
7. Un identifiant unique. Il doit y avoir une colonne ou une combinaison de colonnes permettant didentifier chaque ligne. La méthode la plus simple pour créer un tel identifiant est de créer une colonne supplémentaire dans laquelle vous ajoutez un chiffre qui sincrémente à chaque ligne. Vous pouvez le faire dans Excel en ajoutant les deux premiers chiffres de la série, sélectionnez les deux cellules, et faire glisser le coin droit de la seconde cellule tout en bas du jeu de données pour étendre automatiquement la série. Si le jeu de données a trop de colonnes pour un logiciel comme Excel, vous pouvez utiliser [mySQL.](http://stackoverflow.com/questions/16113570/how-to-add-new-column-to-mysql-table "mySQL")
8. Les dates dans le bon format. Les dates doivent toutes être dans le format “aaaa-mm-jj”
9. Les nombres dans le bon format. Les nombres doivent contenir uniquement des chiffres et éventuellement un point (pas une virgule) pour séparer les décimales (un nombre écrit “12,345.67” devrait être converti en “12345.67”).
La communauté OpenSpending a rassemblé des [exemples](https://drive.google.com/a/okfn.org/#folders/0B_dkMlz2NopEbmRoTExsMDFMR2M "exemples de formatage de données") afin dillustrer les «bons» et les «mauvais» exemples de formatage de jeux de données.
Voici des exemples de jeux de données **mal formattés**:
* [Plusieurs transactions, une ligne](https://docs.google.com/a/okfn.org/spreadsheet/ccc?key=0AvdkMlz2NopEdG5kR0kzQ0E5V3BuTS16MndBT3dMdEE#gid=0 "Plusieurs transactions, une ligne") (plusieurs années sur une seule entrée)
* [Format de chiffre incorrect](https://docs.google.com/a/okfn.org/spreadsheet/ccc?key=0AvdkMlz2NopEdEo1Y2p2R0VvdnJvRXMwUVREbHRoLXc#gid=0 "Format de chiffre incorrect") (les chiffres contiennent des virgules pour la lisibilité)
Voici des exemples de **bons** jeux de données:
* [Washington, DC](https://docs.google.com/a/okfn.org/spreadsheet/ccc?key=0AvdkMlz2NopEdDhrZnRkWl9ZX2ZZNVptTzdueWw3emc#gid=0 "Données de dépenses de la ville de Washington D.C.") (données de dépenses)
* [Minsk, Biélorussie](https://docs.google.com/a/okfn.org/spreadsheet/ccc?key=0AvdkMlz2NopEdEtIMFlEVDZXOWdDUEthUTQ0c21aV2c#gid=0 "Données budgétaires de la ville de Minsk") (données budgétaires)
&nbsp;
**Suivant**:[<a href="../publier-les-donnees-sur-le-web/">Publier les données sur le web</a>]
&nbsp;
**Précédent**: [<a href="../rassembler-les-donnees/">Rassembler les données</a>]

View File

@ -1,20 +0,0 @@
---
section: help
lead: true
title: Inclure une visualisation sur votre site web
authors:
- lombardoelisabetta
---
Il est très simple dinsérer dans votre site Web des visualisations créées grâce à OpenSpending. Votre site bénéficiera alors des visualisations interactives complètes.
Imaginons que vous ayez choisi une visualisation particulière sur la plateforme OpenSpending. Remarquez le bouton **Embed** en bas à droite de la page. En cliquant sur ce bouton, vous accédez au code nécessaire à linclusion de la visualisation sur votre site Web, ainsi quà quelques options concernant la taille (en pixels) de la visualisation interactive. Il ne vous reste quà copier et coller le code dans votre site. Si vous nêtes pas sûr de savoir comment coller le code correctement, contactez ladministrateur de votre site.
Linclusion de visualisations est rendue possible par les _widgets_. Ces morceaux de code que vous pouvez inclure dans une page Web récupèrent à distance des données — dans notre cas, depuis la base de données OpenSpending — sans que vous ayez à stocker vous-même ces données.
&nbsp;
**Précédent**:[<a href="../creer-une-visualisation/">Créer une visualisation</a>]
&nbsp;
**Début**: [<a href="../">Le Guide OpenSpending</a>]

View File

@ -1,41 +0,0 @@
---
section: help
lead: true
title: Le Guide OpenSpending
authors:
- Anders Pedersen
---
Le guide OpenSpending introduit le processus de découverte, recueil et usage des données sur la plateforme OpenSpending.
<strong>Introduction</strong>
<ul>
<li><a href="fr/quest-ce-que-openspending/">Quest ce que OpenSpending?</a></li>
<li><a href="fr/quels-types-de-donnees-financieres-peut-on-ajouter-dans-openspending/">Quels types de données financières peut-on ajouter dans OpenSpending?</a></li>
<li><a href="fr/representation-des-donnees-dans-openspending/">Représentation des données dans OpenSpending</a></li>
</ul>
<strong>Ajouter des données dans OpenSpending</strong>
<ul>
<li><a href="fr/ajouter-des-donnees-dans-openspending/">Ajouter les données</a></li>
<li><a href="fr/rassembler-les-donnees/">Rassembler les données</a></li>
<li><a href="fr/formatter-les-donnees/">Formatter les données</a></li>
<li><a href="fr/publier-les-donnees-sur-le-web/">Publier les données sur le web</a></li>
<li><a href="fr/creer-un-jeu-de-donnees-sur-openspending/">Créer un jeu de données sur OpenSpending</a></li>
<li><a href="fr/modeliser-vos-donnees-dans-openspending/">Modéliser vos données dans OpenSpending</a></li>
</ul>
<strong> Visualisation</strong>
<ul>
<li><a href="fr/creer-une-visualisation/">Créer une visualisation</a></li>
<li><a href="fr/inclure-une-visualisation-sur-votre-site-web/">Inclure une visualisation sur votre site web</a></li>
</ul>
&nbsp;
Début: [<a href="fr/quest-ce-que-openspending/">Quest ce que OpenSpending?</a>]
Up: [<a href=".fr/">Autres guides</a>]
&nbsp;
&nbsp;

View File

@ -1,82 +0,0 @@
---
section: help
lead: true
title: Modéliser vos données dans OpenSpending
authors:
- lombardoelisabetta
---
Pour charger vos données dans OpenSpending, vous devez construire un modèle de vos données. Un modèle définie comment les données seront comprises par OpenSpending. Lapplication représente les propriétés des données en termes de _dimensions_. Modéliser les données consiste à lister les dimensions que vous souhaitez inclure dans le jeu de données OpenSpending et définir pour chaque dimension la ou les colonnes correspondantes dans le jeu de données dorigine.
###Dimensions obligatoires : montant et date
Chaque modèle nécessite au moins deux dimensions : un montant et une date. Elles spécifient la taille de la transaction et le moment de sa réalisation. Le montant et la date sont associés à des indicateurs spécifiques. Le montant est représenté par un indicateur de mesure et le temps est représenté par une date. Des dimensions génériques ne peuvent pas représenter ces valeurs particulières.
Lorsque vous modélisez vos données, ce nest pas une mauvaise idée de commencer par les dimensions obligatoires. Tout dabord, cliquez sur le menu _**Dimensions &amp; Measures**_ au sein de la page _**Manage the dataset**_ de votre jeu de données.
![Dimensions obligatoires](http://blog.openspending.org/files/2013/08/image_5-e1375888673131.png)
Après, cliquez sur _**Add Dimension**_ pour faire apparaître le panneau _Add new dimension_. Cliquez sur la case correspondant à _Date_. Vous verrez la fenêtre _Name_ automatiquement complétée avec la date, comme ci-dessous. Cliquez sur le bouton vert _**Add**_.
![Ajouter une nouvelle dimension](http://blog.openspending.org/files/2013/08/image_6-e1375888703851.png)
Le prochain écran que vous verrez vous donnera des informations sur la signification du temps. Dans la liste déroulante à coté de _Column_, sélectionnez la colonne de vos données qui représentera la valeur du temps.
![Temps](http://blog.openspending.org/files/2013/08/image_7-e1375888730762.png)
Quand vous avez identifié la colonne du temps, cliquez sur _**Add Dimension**_ encore une fois pour ajouter le montant. Cette fois-ci, sélectionnez la case correspondant à _Measure_, qui sera automatiquement complétée sous le nom “montant” et cliquez sur _**Add**_. Choisissez la colonne représentant la valeur de la transaction dans la liste déroulante à côté de _Column_.
###Autre dimension indispensable: lidentifiant
Il y a une autre dimension nécessaire au fonctionnement du modèle: la dimension (ou groupe de dimensions) dont la valeur identifie de manière unique chaque entrée de données, la clé.
Une entrée peut être identifiée aussi par une combinaison de plusieurs colonnes, une _dimension composée_. Étant donné que les clés peuvent être composées, le type “dimension composée” doit être utilisé pour les répresenter, même si votre clé en loccurrence nest pas composée.
La dimension clée peut être assignée en cliquant sur **Add Dimension** et ensuite en sélectionnant le bouton radio _Dimension_.
Ajoutez le nom de votre clé, par exemple “clé”, dans la case appropriée. Cliquez sur **Add**. Cochez la case _include in unique_ afin didentifier cette dimension en tant que partie de votre clé.
On passe maintenant à la liste de **Fields**, qui contient deux rangées nommées _name_ (nom) et _label_ (étiquette). Une dimension composée peut contenir un nombre arbitraire de champs _(fields)_, chacun ayant un nom et un type et pouvant être associé à une colonne dans vos données. Cela nous permet dexpliquer le sens du mot “composé” dans ce contexte: les dimensions sont “composées” car elles regroupent plusieurs colonnes de données dans une seule propriété du jeu de données.
![Dimensions composées](http://blog.openspending.org/files/2013/08/image_8-e1375888755790.png)
Une dimension composée nécessite au moins deux champs, _name_ et _label_. Ces derniers doivent être de type _id_ et _string_. Le nom de la dimension est utilisé afin de fournir à cette dernière une URL opérationnelle et létiquette _(label)_ est utilisée pour présenter la dimension au sein de linterface.
Pour créer une dimension composée minimale, il suffit dassocier la même colonne de vos données dorigine avec le nom _(name)_ et létiquette _(label)_. Choisissez la colonne appropriée pour chacun et laissez les options sous “types” unchangées.
###Mesures et autres dimensions
Avec un montant, une date et un identifiant, votre modèle est suffisamment riche. Toutefois, un modèle parfaitement abouti devrait inclure une dimension pour chaque caractéristique importante du jeu de données dorigine. Suivre certaines conventions est alors utile.
Une pratique courante dans la présentation des jeux de données dorigine est la segmentation de linformation qui caractérise chaque donnée sur de multiples colonnes. Linformation concernant un compte associé à une transaction peut être réparti entre une colonne “Compte” qui identifie le montant chiffré et une colonne “Description du compte” qui donne une description écrite. Limage ci-dessous illustre cette pratique avec les colonne “Head-account” et “Sub-account”.
![Head-accounts et sub-accounts]({{ site.baseurl }}/img/blog/2013/08/image_9.png)
Les dimensions composées dOpenSpending sont conçues pour modéliser ce type dinformations éparpillées. Pour ce faire, ajoutez une nouvelle dimension composée et associez chaque colonne à lun des champs de la dimension. Essayez de faire correspondre une colonne aux commentaires très détaillés à label et une colonne plus concise à _name_. Dans limage ci-dessous “Head-account” correspond à _name_ et “Head-account description” à _label_.
![Head-account](http://blog.openspending.org/files/2013/08/image_10-e1375888789463.png)
Certaines colonnes de votre base de données sont plus indépendantes, représentant certaines propriétés particulières de chaque donnée. Par exemple, une colonne qui attribue à chaque transaction une catégorie rentre dans ce cadre. Dans limage ci-dessous, les colonnes _Reporting Type_, _Revenue/Expenditure_ et _Recurrent/Investment_ sont de ce type.
![Propriétés des données]({{ site.baseurl }}/img/blog/2013/08/image_11.png)
Les colonnes indépendantes qui spécifient des propriétés ou des catégories sont mieux modélisées avec des dimensions dattribut. Un attribut est essentiellement une dimension qui ne connaît quun seul champ, quel que soit son type. Pour créer un attribut, sélectionnez simplement la case _Attribute_ quand vous ajoutez une dimension.
![Attribute](http://blog.openspending.org/files/2013/08/image_12-e1375888823415.png)
###Pour finir : sauvegarder et charger
Lorsque chaque dimension a été spécifiée et reliée aux colonnes dans les données source, cliquez sur **Save Dimensions** pour sauvegarder votre modèle. En cas derreurs, un message apparaîtra, vous demandant de corriger certains paramètres. Sil ny a pas derreurs, vous serez rédirigé vers le Dashboard, où vous pourrez charger vos données.
Une fois que les données ont été téléchargées, le modèle que vous avez créé sera figé et vous naurez plus la possibilité de léditer. Cest pourquoi il est préférable de tester le modèle avant de télécharger les données. Pour ce faire, cliquez sur **Test a sample** dans votre tableau de sources de données. Attendez quelques secondes et rechargez la page. Si vous voyez un message indiquant COMPLETE sur un fond vert, alors votre modèle est prêt. Si vous voyez un message indiquant ERRORS, des corrections sont nécessaires.
![Sauvegarder et charger](http://blog.openspending.org/files/2013/08/image_13-e1375888848457.png)
Si votre modèle ne contient plus derreurs, cliquez sur **Load** pour charger le jeu de données source et lui appliquer le modèle. Vous pouvez ensuite retourner sur la page daccueil du jeu de données en cliquant sur son nom en haut de lécran et créer des visualisations ou explorer le jeu de données.
&nbsp;
**Suivant**:[<a href="../creer-une-visualisation/">Créer une visualisation</a>]
&nbsp;
**Précédent**: [<a href="../creer-un-jeu-de-donnees-sur-openspending/">Créer un jeu de données sur OpenSpending</a>]

View File

@ -1,35 +0,0 @@
---
section: help
lead: true
title: Publier les données sur le web
authors:
- lombardoelisabetta
---
Les données ne peuvent pas encore être téléchargées directement sur OpenSpending. Pour publier des données, celles-ci doivent mises à disposition sur le web.
###Google Drive
Vous pouvez mettre vos données à disposition sur le web en utilisant Google Drive spreadsheet.
1. Importez vos données dans Google Drive. Créez une nouvelle feuille de calcul sur Google Drive, sélectionnez ensuite _Importer_ depuis le menu Fichier. Sélectionnez _Remplacer la feuille de calcul_, cliquez **Choose File**, et choisissez votre fichier CSV. Ensuite, cliquez sur Importer.
2. Assurez-vous que Google Docs formatte correctement votre jeu de données. Sélectionnez la colonne qui contient les dates. Cliquez sur le menu _Format_ et sélectionnez _Nombre -&gt; Autre formats -&gt; 2008-09-26_. Vos dates devraient apparaître dans le format **aaaa-mm-jj** utilisé par OpenSpending.
3. Cliquez sur le menu _Fichier_ et sélectionnez _Publier sur le web_ ... Dans la fenêtre qui apparaît, cliquez sur **Démarrer la publication**. En dessous, dans la section Insérer un lien vers les données publiées, sélectionnez **CSV (valeurs séparées par des virgules)**. LURL en bas est celle de votre jeu de données.
![Publier sur Google Drive]({{ site.baseurl }}/img/blog/2013/08/image_0.png)
###Gist
GitHub Gist est un moyen très pratique pour héberger des fichiers textes de petite taille, notamment des fichiers CSV.
1. Connectez-vous à GitHub (ou enregistrez-vous si vous ne lavez pas déjà fait), allez ensuite sur [gist.github.com](https://gist.github.com "Gist").
2. Cliquez et déplacez votre fichier CSV depuis le répertoire sur votre ordinateur vers la page GitHub Gist dans votre navigateur. Le nom du fichier et son contenu apparaîtront.
3. Cliquez sur _**Create Public Gist**_ pour être redirigé sur la page daccueil de votre nouveau gist. LURL de votre jeu de données est accessible via le bouton “” dans le coin en haut à droite du fichier.
![Publier sur Gist](http://blog.openspending.org/files/2013/08/image_1-e1375888253802.png)
&nbsp;
**Suivant**:[<a href="../creer-un-jeu-de-donnees-sur-openspending/">Créer un jeu de données sur OpenSpending</a>]
&nbsp;
**Précédent**: [<a href="../formatter-les-donnees/">Formatter les données</a>]

View File

@ -1,49 +0,0 @@
---
section: help
lead: true
title: Quels types de données financières peut-on ajouter dans OpenSpending?
authors:
- lombardoelisabetta
---
OpenSpending est très flexible et admet de nombreux type de données financières. Et si le projet OpenSpending sintéresse en particulier aux données financières des gouvernements, cela nest pas une limitation technique. OpenSpending supporte ainsi nimporte quel jeu de données contenant une liste de transactions associées à une quantité monétaire et à une date.
La plupart des données actuellement hébergées sur OpenSpending sont des données budgétaires ou bien des données transactionnelles. La principale différence entre ces deux types de données est leur granularité. Les données transactionnelles portent sur des transactions uniques réalisées, alors que les données budgétaires sont des données de dépenses aggrégées par catégories.
###Les données de transactions financières
Les données transactionnelles, ou plus simplement les “données de dépense”, renseignent sur les transactions financières. Chaque paiement dune entité à une autre entité pour une date donnée et avec un objet particulier (un projet, un service, etc.) est alors renseigné.
Les informations agrégées (ex: un sous-total de transactions) ne devraient pas être incluses dans les données de transactions. Les données qui ont été partiellement ou complétement agrégées ne peuvent pas être analysées de la même manière et devraient donc être traitées comme des données budgétaires plutôt que comme des données transactionnelles. En revanche, si plusieurs paiements ont été effectués à différentes dates pour régler une même dépense, ceux-ci devraient pouvoir être agrégés pour représenter une transaction unique.
Exemples de données transactionnelles sur OpenSpending:
* [Contrats publics Washington D.C.](http://openspending.org/dc-vendors-contractors/ "Contrats publics Washington D.C.")
* [Agence de Développement Autrichienne.](http://openspending.org/ada/ "Agence de Développement Autrichienne")
Un autre type de données concerne les marchés publics. Les données sur les marchés publics informent sur lobjet du marché, le montant du marché, et qui a remporté le marché. Les données des marchés publics peuvent être considérées comme un sous-type de données transactionnelles (dans le cas où il sagit dun marché réalisé).
Exemples de données sur les marchés publics sur OpenSpending:
* [Marchés publics au Sénégal.](http://openspending.org/marches-publics-senegal/views/liste-des-attributaires "Marchés publics au Sénégal")
* [Marchés publics France 2011.](http://openspending.org/marches-publics-france-2011 "Marchés publics France 2011")
###Les données budgétaires
Pour les données budgétaires, les dépenses et les recettes sont agrégées par catégories. Lobjectif de ces aggrégations est daider le lecteur à comprendre le budget, qui est habituellement un document de politique publique utilisé pour donner au lecteur un aperçu des choix financiers les plus importants du gouvernement. Les fonds alloués sont habituellement structurés en fonction de secteurs plutôt quen fonction des actuels bénéficiaires.
Les données budgétaires présentent généralement à la fois les recettes et dépenses passées et le budget prévisionnel. Ainsi, les montants des dépenses pour les années précédentes dans un secteur particulier donnent une indication sur les fonds qui devrait être attribués pour la période suivante. Les données budgétaires sont en général composées de données agrégées et destimations statistiques.
Les pays publient différents types dinformations budgétaires, incluant : le pré-budget, la proposition de budget (projet de loi de Finance), le budget ratifié, et le budget citoyen (qui est une version simplifiée du budget pour une meilleure compréhension par les citoyens).
Des exemples de données budgétaires sur OpenSpending:
* [Budget de la ville de Berlin.](http://openspending.org/berlin_de "Budget de la ville de Berlin")
* [Budget de la ville de Séville.](http://openspending.org/seville-budget "Budget de la ville de Séville")
&nbsp;
**Suivant**:[<a href="../representation-des-donnees-dans-openspending/">Représentation des données dans OpenSpending</a>]
&nbsp;
**Précédent**: [<a href="../quest-ce-que-openspending/">Quest ce que OpenSpending?</a>]

View File

@ -1,18 +0,0 @@
---
section: help
lead: true
title: Quest ce que OpenSpending?
authors:
- lombardoelisabetta
---
OpenSpending est une application web et une communauté de contributeurs dont lobjectif est de répertorier toutes les transactions financières des entreprises et gouvernements à travers le monde et présenter ces données de manière utile et engageante. OpenSpending est un projet ouvert maintenu par la communauté. Toutes les personnes intéressées par les données financières de toutes sortes sont invitées à contribuer à la base de données OpenSpending, à créer des visualisations en utilisant lapplication OpenSpending, et à utiliser lAPI OpenSpending.
Ce chapitre introduit les concepts principaux du système aux nouveaux contributeurs dOpenSpending. Il décrit les différents types de données financières que OpenSpending supporte, et explique comment lapplication représente les données.
&nbsp;
**Suivant**:[<a href="../quels-types-de-donnees-financieres-peut-on-ajouter-dans-openspending/">Quels types de données financières peut-on ajouter dans OpenSpending?</a>]
&nbsp;
**Précédent**: [<a href="../">Le Guide OpenSpending</a>]

View File

@ -1,20 +0,0 @@
---
section: help
lead: true
title: Rassembler les données
authors:
- lombardoelisabetta
---
Pour ajouter un jeu de données sur OpenSpending, vous devez déjà avoir des données à disposition. Si ce nest pas le cas, vous devrez chercher une source de données sur le web (ou en ajouter une).
Commencez votre recherche en consultant les ressources telles que [School of Data](http://schoolofdata.org/handbook/courses/finding-data/ "School of data") (en anglais) ou le [Guide du Datajournalisme](http://jplusplus.github.io/guide-du-datajournalisme/ "Guide du Datajournalisme"). Consultez également les sources officielles de données telles que [data.gouv.fr](http://www.data.gouv.fr "Données officielles du gouvernement français"). Vous pouvez également trouver de laide et des idées en vous rendant sur le groupe [OpenSpending](http://datahub.io/group/openspending "OpenSpending") sur datahub.io, ou en posant des questions sur le channel IRC #openspending sur Freenode.
Si les données trouvées ne sont pas dans un format lisible par une machine, comme un fichier Excel ou CSV, mais dans un format du type PDF ou Word, il sera très difficile de travailler avec. Vous devriez alors peut être penser à essayer avec dautres données.
&nbsp;
**Suivant**:[<a href="../formatter-les-donnees/">Formatter les données</a>]
&nbsp;
**Précédent**: [<a href="../ajouter-des-donnees-dans-openspending/">Ajouter des données dans OpenSpending</a>]

View File

@ -1,32 +0,0 @@
---
section: help
lead: true
title: Représentation des données dans OpenSpending
authors:
- lombardoelisabetta
---
OpenSpending contient une collection de jeux de données, dont chacun provient dune source différente. Dans un jeu de données, chaque transaction est représentée par une liste dentrées (ou attributs). Chaque jeu de données a son propre modèle de structure de données. Un modèle décrit les propriétés dun jeu de données en termes de dimensions.
###Jeu de données
Lunité de base dans OpenSpending est le jeu de données. Les données financières partageant un sujet commun (un budget pour une ville, le budget dune année) sont regroupées ensemble et enregistrées dans un jeu de données. Un jeu de données est une collection d «entrées», et chaque entrée représente une transaction unique associée à un montant et à une date.
Un jeu de données inclut également des métadonnées pour décrire son contenu. Les métadonnées contiennent une description du jeu de données, des informations sur la source des données, et dautres informations qui peuvent aider lutilisateur à trouver et interpréter le contenu de ce jeu de données.
###Modèle
Le créateur dun jeu de données a la maîtrise compléte sur la structure du jeu de données. Cette structure est créée en définissant un modèle qui est composé de dimensions.
Une dimension est une propriété qui définit une entrée du jeu de données. En considérant quun jeu de données est un tableau, les dimensions peuvent alors être assimilées aux colonnes. Cependant, une dimension peut avoir une structure plus complexe quune seule colonne.
Les dimensions sont de différentes natures. La plus importante étant celle de type _**measure**_ (montant). Une _measure_ est une dimension qui contient une valeur numérique. Une autre dimension importante est celle de type _**time_** (temps), qui représente la date et lheure. Chaque entrée nécessite au moins une dimension _measure_ et une dimension _time_, représentant respectivement le montant et la date dune transaction.
Les autres dimensions sont utilisées pour représenter dautres propriétés du jeu de données, telles que le numéro de la transaction, le type de la transaction, le nom de ladministration et le nom de la société ou lindividu impliqué. Ces dimensions incluent des _**attributes**_ (attributs), qui contiennent une valeur unique, et les _**compound dimensions**_ (dimensions composées), qui peuvent contenir plusieurs valeurs. Les dimensions composées peuvent être utiles lorsquune propriété est définie par plusieurs autres propriétés.
&nbsp;
**Suivant**:[<a href="../ajouter-des-donnees-dans-openspending/">Ajouter des données dans OpenSpending</a>]
&nbsp;
**Précédent**: [<a href="../quels-types-de-donnees-financieres-peut-on-ajouter-dans-openspending/">Quels types de données financières peut-on ajouter dans OpenSpending?</a>]

View File

@ -5,16 +5,23 @@ title: The OpenSpending Guide
authors:
- Neil Ashton
---
The OpenSpending Guide is the complete manual for OpenSpending. Whether you wish to find, clean, add, or use spending data with OpenSpending we've got you covered.
The OpenSpending Guide is the manual for OpenSpending, covering the entire process of finding, adding, and using data with OpenSpending in detail.
The OpenSpending Guide is available in multiple languages:
* Introduction
* [What is OpenSpending?](./what-is-openspending)
* [What types of financial data can go into OpenSpending?](./financial-data-types)
* [How does OpenSpending represent data?](./data-model)
* Adding Data to OpenSpending
* [Overview](./adding-data-overview)
* [Gathering data](./gathering-data)
* [Formatting data](./formatting-data)
* [Publishing data on the web](./publishing-data)
* [Creating a dataset on OpenSpending](./creating-dataset)
* [Modelling your data in OpenSpending](./modelling-data)
* Visualizations
* [Create a visualization](./create-viz)
* [Embed a visualization on your website](./embed-viz)
* [English](guide/en)
* [French](guide/fr)
* [Italian](guide/it)
* [Romanian](guide/rom)
* [Spanish](guide/esp)
* [Turkish](guide/tur) (still in progress)
* <a href="https://trello.com/c/abfAVgBC/14-permanent-openspending-guide-translation">Add your language</a> by translating the guide available in <a href="https://docs.google.com/a/okfn.org/document/d/1-RhyBc7rFgBW78160BA0mxD6cpVZ-PtfM8QL_WEoPqY/edit#heading=h.79vyyu8n203g">this Google Doc</a>!
**Begin**: [What is OpenSpending?](./what-is-openspending/)
**Up**: [OpenSpending help](../)
**Up**: [OpenSpending guides](../)

View File

@ -1,28 +0,0 @@
---
section: help
lead: true
title: Aggiungere dati
authors:
- stefanobandera
---
Uno dei contributi più importanti per il progetto OpenSpending è quello di aggiungere un nuovo dataset. Questa sezione della guida vi spiegherà il processo tramite cui aggiungere nuovi dati.
Un flusso di lavoro tipico per l'importazione di un insieme di dati in OpenSpending prevede le seguenti fasi:
* raccogliere dati leggibili da una fonte attendibile
* Convertire i dati in un file CSV nel formato atteso dal OpenSpending, pulendolo per rimuovere incongruenze ed errori
* Pubblicare i dati sul web
* Creare un dataset aggiungendo i dati pubblicati come nuova fonte
* Modella il dataset per assegnare un ruolo ad ogni colonna presente nella tabella di origine
* Caricare o rifinisci i dati in base al feedback fornito dalla piattaforma, in base alla coerenza dei propri dati.
Ognuno di questi passi sarà spiegato in dettaglio nelle seguenti sezioni.
**Prossimo** [<a href="../raccolta-dati/">Raccolta dati</a>]
**Precedente** [<a href="../come-vengono-rappresentati-i-dati-su-openspending/">Come vengono rappresentati i dati su OpenSpending?</a>]

View File

@ -1,26 +0,0 @@
---
section: help
lead: true
title: Come vengono Rappresentati i dati su OpenSpending
authors:
- stefanobandera
---
OpenSpending mantiene una collezione di dataset, ciascuna delle quali rappresenta un insieme di dati derivanti da una sorgente separata. All'interno di ogni set di dati, le transazioni individuali sono rappresentate da un insieme di voci. Ogni set di dati ha un modello proprio che mappa la struttura dei dati stessi. Il modello codifica le proprietà di ogni set in termini di dimensioni.
#### Dataset
L'unità di base del sistema OpenSpending è il dataset. Le transazioni finanziarie che condividono un tema comune (ad esempio, la spesa di una particolare città, un bilancio per un determinato anno) vengono raggruppate e memorizzate come un set di dati. Un dataset è una raccolta di "voci", e ogni voce rappresenta una singola transazione associata ad una quantità di soldi e ad un periodo di tempo.
### Modelli
La struttura di ogni dataset dipende completamente dal suo creatore. Questa struttura viene creata specificando un modello che fornisce le dimensioni lungo le quali i vari item possono differire l'uno dall'altro.
Un modello consiste in un insieme di dimensioni. Una dimensione è una proprietà che potenzialmente differenzia una voce da un altra. Se pensate ad un dataset come un foglio di calcolo, ciascuna dimensione può essere pensata come una colonna. Tuttavia le dimensioni possono avere più struttura di una normale colonna del foglio di calcolo.
Le dimensioni sono di diversi tipi. La più importante è il tipo di misura. Le misure sono dimensioni che possono contenere un singolo valore numerico. Un' altra importante tipologia di dimensione è il tempo, che rappresenta date e orari. Ogni dato necessita di almeno una misura e una dimensione di tempo, che rappresentano rispettivamente la quantità di denaro presente nella transazione e il momento in cui ha avuto luogo.
Le rimanenti tipologie di dimensioni sono utilizzate per rappresentare altre proprietà che gli item potrebbero avere, ad esempio: numeri di transazione, etichette provenienti da uno schema di classificazione,, oppure i nomi delle persone o le società coinvolte. Tali dimensioni comprendono gli attributi, che possono contenere un singolo valore, e le dimensioni composte, che possono contenere un insieme nidificato di valori. Le dimensioni composte sono utili quando una proprietà include diverse sotto-proprietà che potrebbero essere utilizzate per aggregare i dati.
**Prossimo** [<a href="../aggiungere-dati-ad-openspending/">Aggiungere dati ad OpenSpending</a>]
**Precedente** [<a href="../quali-tipi-di-dati-finanziari-entrano-in-openspending/">Quali tipi di dati finanziari entrano in OpenSpending?]</a>

View File

@ -1,14 +0,0 @@
---
section: help
lead: true
title: Cos'è OpenSpending?
authors:
- stefanobandera
---
OpenSpending è una comunità di condivisione dati e applicazioni web che mira a tracciare ogni transazione finanziaria di governi e aziende di tutto il mondo, presentando i dati in una forma utile e coinvolgente. OpenSpending è un progetto* open* gestito da una comunità di contributori. Chiunque sia interessato a sfruttare dati di qualsiasi tipo è invitato a contribuire al database OpenSpending, a creare visualizzazioni con il software OpenSpending, e a utilizzare le API messe a disposizione.
Questo capitolo introduce i nuovi collaboratori OpenSpending ai concetti fondamentali del sistema. Esso descrive il tipo di dati finanziari che OpenSpending supporta, e spiega come vengano rappresentati i dati.
**Prossimo** [<a href="../quali-tipi-di-dati-finanziari-entrano-in-openspending/">Quali tipi di dati entrano in OpenSpending?</a>]
**Precedente** [<a href="{{site.baseurl}}/help/guide/it/">Guida ad OpenSpending</a>]

View File

@ -1,52 +0,0 @@
---
section: help
lead: true
title: Crea una visualizzazione
authors:
- stefanobandera
---
La piattaforma OpenSpending rende facile creare e incorporare visualizzazioni di set di dati. Sono supportati tre tipi di visualizzazioni: BubbleTree, TreeMap, e la tabella di aggregati.
Tutte le visualizzazioni presenti in OpenSpending permettono di scegliere una serie di dimensioni lungo le quali aggregare i dati, aumentando il livello di particolarità qualora ve ne sia bisogno. Ogni visualizzazione viene creata nello stesso modo: con la scelta delle dimensioni da aggregare e l'ordine in cui eseguire il drill down.
Per iniziare a creare una visualizzazione, vai alla home page di un dataset e seleziona **Create a visualization** dal <em>Visualizations menu</em>.
**BubbleTree**
Il BubbleTree è una visualizzazione interattiva che presenta i dati di spesa aggregati come un cerchio a bolle. Ogni bolla rappresenta un aggregato (sub) totale. La bolla centrale rappresenta una somma aggregata, e le sue bolle circostanti rappresentano le altre somme da cui è composta. Cliccando su ogni bolla, viene mostrato all'utente come la somma si divida ulteriormente in sub-totali .
Per creare un BubbleTree, scegli le dimensioni da aggregare e l'ordine in cui aggregarle. Scegli la dimensione primaria dal menu a cascata. Si vedrà il totale aggregato per quella dimensione, come la bolla centrale, con i singoli valori totali che la circondano.
<img class="alignnone size-medium wp-image-1578" src="http://community.openspending.org/files/2013/09/image_14-259x300.png" alt="image_14" width="259" height="300" />
Per aggiungere un secondo livello, clicca su Add a level e scegli una nuova dimensione. Gli utenti saranno ora in grado di fare clic su le bolle di "drill-down" e vedere come i valori del primo livello si suddividono in i singoli valori totali nel secondo livello.
<img class="alignnone size-medium wp-image-1579" src="http://community.openspending.org/files/2013/09/image_15-251x300.png" alt="image_15" width="251" height="300" />
**TreeMap**
Il TreeMap presenta i dati di spesa aggregati come un rettangolo interattivo composto da rettangoli colorati. Ogni rettangolo rappresenta valori aggregati per una particolare dimensione dei dati. Cliccando su "zooms in" si mostra come è possibile scomporre ed esplorare le dimensioni aggregate.
Per creare un TreeMap, basta scegliere le dimensioni da aggregare e il loro ordine. Seleziona la dimensione primaria dal menu *Tile*. Vedrai un TreeMap che mostra come la spesa totale scompone attraverso quella dimensione.
<img class="alignnone size-medium wp-image-1580" src="http://community.openspending.org/files/2013/09/image_16-262x300.png" alt="image_16" width="262" height="300" />
La visualizzazione non è ancora interattiva . L'aggiunta di ulteriori livelli ci mostra come sia possibile scomporre ed esplorare le dimensioni aggregate, permettendo di visualizzare in dettaglio come valori aggregati si scompongano in unità più piccole. Per aggiungere un secondo livello di rettangoli, clicca su **Add a level** e scegli una nuova dimensione. Gli utenti possono ora scegliere i rettangoli con i quali dividere il totale.
<img class="alignnone size-medium wp-image-1581" src="http://community.openspending.org/files/2013/09/image_17-254x300.png" alt="image_17" width="254" height="300" />
### Table of Aggregates
La tabella di aggregati è una semplice rappresentazione tabellare di un dataset che aggrega i totali delle dimensioni scelte. Una tabella di aggregati si specifica scegliendo dimensioni da inserire nelle sue colonne.
La scelta di una dimensione primaria tramite il *Column menu* visualizza i dati in forma di tabella, con importi aggregati e percentuali del totale complessivo. Di default, le righe verranno ordinati in base a valori percentuali.
<img class="alignnone size-medium wp-image-1582" src="http://community.openspending.org/files/2013/09/image_18-300x229.png" alt="image_18" width="300" height="229" />
Aggiungendo un'altra colonna, cliccando su Add a level, si rompe ogni subtotale presente nella prima colonna tramite le somme aggregati della nuova colonna. Nota che questo in genere cambia i valori percentuali e riorganizza le righe della tabella.
<img class="alignnone size-medium wp-image-1583" src="http://community.openspending.org/files/2013/09/image_19-300x264.png" alt="image_19" width="300" height="264" />
**Prossimo** [<a href="../inserisci-una-visualizzazione-nel-tuo-sito-web/">Inserisci una visualizzazione nel tuo sito web</a>]
**Precedente** [<a href="../creare-un-dataset-su-openspending/">Modellare i tuoi dati su OpenSpending</a>]

View File

@ -1,54 +0,0 @@
---
section: help
lead: true
title: Creare un dataset su OpenSpending
authors:
- stefanobandera
---
Per iniziare la condivisione dei dati sulla piattaforma OpenSpending, registrati su OpenSpending.org e crea un nuovo OpenSpending dataset. Per creare un set di dati, è sufficiente compilare alcuni metadati, che caratterizzano i propri dati, e fornire l'URL in cui i dati sono ospitati.
**Crea un nuovo dataset**
Accedi OpenSpending.org con le informazioni utente, o registrati se non l'hai ancora fatto. Arriverai alla Dashboard, dove vedrai un pulsante blu etichettato *Import a dataset. *Fai clic qui per avviare la creazione di un nuovo insieme di dati su OpenSpending.
La schermata successiva richiede di fornire i metadati che contraddistinguono i tuoi dati. Questo include i seguenti campi:
* *Title*: un nome descrittivo e significativo per il dataset. Può essere qualsiasi stringa.
* *Identifier*: un titolo più breve, utilizzato come parte dell'URL del dataset. Può contenere solo caratteri alfanumerici, trattini e underscore - senza spazi o segni di punteggiatura.
* *Category*: "Bilancio", "spese", e "Altro". Vedi la sezione guida riguardo i tipi di dati finanziari per i dettagli su queste categorie.
* Currency: la valuta in cui la spesa descritta dal dataset ha luogo.
* Paesi: l'elenco dei paesi di riferimento nel dataset. La scelta degli Stati è limitata da un elenco di paesi validi.
* Languages: l'elenco delle lingue utilizzate nel dataset. La scelta delle lingue è limitata da un elenco di lingue valide.
* Description: una caratterizzazione del dataset in semplice prosa. Può essere qualsiasi stringa.
Compila tutti questi campi. Assicurati di includere una descrizione che spieghi l'origine del dataset e riconosca tutte le modifiche introdotte (ad esempio, le operazioni di pulizia che hai fatto).
Una volta che tutti i metadati sono state inseriti, premi **Next Step **per procedere.
**Aggiungere una nuova fonte di dati**
Facendo clic su *next step* viene creato il tuo nuovo dataset OpenSpending che ti conduce alla pagina di Gestione. La pagina Gestione viene utilizzata per aggiungere sorgenti dati. E' utilizzato anche per fornire informazioni schematiche che permettono ad OpenSpending di interpretare i dati, un processo chiamato "modelling" che verrà trattato nella prossima sezione della guida.
Per aggiungere una fonte di dati al dataset, fai clic su *Add source*. Viene visualizzato un messaggio che vi chiederà di inserire un URL. Fornisci l'URL del file CSV che hai pubblicato sul web, nella sezione precedente della guida e fai clic su *Create*. Vedrai una finestra di testo blu che indica lelaborazione dati di OpenSpending.
<img class="alignnone size-medium wp-image-1564" src="http://community.openspending.org/files/2013/09/image_2-300x104.png" alt="image_2" width="300" height="104" />
Clicca su *Refresh *o semplicemente usa il pulsante di aggiornamento del browser. Se OpenSpending riesce ad analizzare i dati, si dovrebbe vedere una casella di testo verde che indica che i dati sono pronti. Si dovrebbe anche vedere un elenco delle colonne del file CSV.
<img class="alignnone size-medium wp-image-1565" src="http://community.openspending.org/files/2013/09/image_3-300x202.png" alt="image_3" width="300" height="202" />
Si noti che se si fornisce ad OpenSpending un file HTML invece di un file CSV valido, il software non si lamenterà, ma semplicemente cercherà di analizzare il codice HTML come se fosse un file CSV. Il risultato è simile al seguente.
<img class="alignnone size-medium wp-image-1566" src="http://community.openspending.org/files/2013/09/image_4-300x104.png" alt="image_4" width="300" height="104" />
Se hai aggiunto una fonte dati non corretta, non ti preoccupare. Non è necessario utilizzare la fonte nel dataset finale: OpenSpending richiede di lavorare molto su una fonte di dati prima di poter essere pubblicata. Basta aggiungere una nuova, corretta sorgente e dimenticare quello sbagliata.
**Prossimo** [<a href="../modellare-i-tuoi-dati-su-openspending/">Modellare i tuoi dati su OpenSpending</a>]
**Precedente** [<a href="../pubblicare-i-dati-sul-web/">Pubblicare i dati sul web</a>]

View File

@ -1,52 +0,0 @@
---
section: help
lead: true
title: Formattare i dati
authors:
- stefanobandera
---
Openspending si aspetta che tutti i dati sian presentati in un formato semplice.
**CSV**
OpenSpending accetta dati in un formato di file singolo, il Comma Separated Value (CSV). CSV è un file di testo che rappresenta i dati in forma di tabella, simile a un foglio di calcolo. In una tabella, ciascun dato è rappresentato da una riga, e le proprietà di ciascun dato sono rappresentate da una colonna. I file CSV codificano le tabelle dando ad ogni riga una linea nel file di testo e separando le colonne tramite virgole.
I CSV accettati da OpenSpending sono denormalizzati, il che significa che essi non liberano spazio eliminando i valori ridondanti. Inoltre file CSV di OpenSpending sono a forma rettangolare, ovvero posseggono esattamente lo stesso numero di colonne in ogni riga.
**Il format OpenSpending**
I file CSV di OpenSpending devo avere le seguenti proprietà.
1. Una riga di intestazione. La prima riga del file CSV deve contenere i nomi delle colonne, separati da virgole. Tutte le altre righe vengono trattate come righe dei dati.
2. Almeno tre colonne. Valori indispensabili delle colonne sono: un importo, la data (in cui ci si potrebbe riferire solo allanno), e un compratore o un destinatario (che potrebbe essere il nome di un account).
3. Colonne corrispondenti. Ogni colonna deve rappresentare sempre un singolo tipo di valore per tutte le righe. (Non ci può essere nessuna riga sottointestata, per esempio.)
4. Le righe sono punti dati. Le righe devono contenere un solo tipo di informazione: una transazione o una linea di bilancio. Una riga deve rappresentare al massimo un solo periodo di tempo. (Le righe non possono rappresentare più transazioni.)
5. Nessun riga o cella vuota. Ogni riga di un file di dati importato deve contenere tutte le informazioni necessarie per costruire l'oggetto risultante.
6. Nessun totale pre-aggregato (per es. totali parziali o "roll-up"). OpenSpending calcolerà questi automaticamente.
7. Un identificatore unico. Ci deve essere una colonna (o una combinazione di colonne) il cui valore identifica in modo univoco ogni riga. Il modo più semplice per creare un tale identificativo è quello di aggiungere una colonna fittizia al dataset in cui si inserisce un numero che per ciascuna riga aumenta. È possibile farlo in Excel digitando i numeri nelle prime due file, selezionando entrambe le celle e trascinando verso il basso l'angolo inferiore destro della cella per estendere la serie. Se l'insieme dei dati ha troppe righe per Excel, è possibile aggiungere la colonna di identificazione unica usando [mySQL](http://stackoverflow.com/questions/16113570/how-to-add-new-column-to-mysql-table).
8. Le date nel formato corretto. Le date devono essere nel formato "aaaa-mm-gg".
9. I numeri nel formato corretto. I numeri devono contenere solo cifre e un periodo opzionale - no virgole! (Numeri leggibili come "12,345.67" devono essere convertiti in numeri come "12.345,67".)
La comunità di OpenSpending ha raccolto qualche esempio di fogli di calcolo, al fine di illustrare come appaiono "buoni"o “cattivi” dati tabulari.
Qui sono presentati esempi di cattiva formattazione dei fogli di calcolo:
* [transazioni multiple su una riga](https://docs.google.com/spreadsheet/ccc?key=0AvdkMlz2NopEdG5kR0kzQ0E5V3BuTS16MndBT3dMdEE#gid=0), (diversi anni su di una stessa riga)
* [cattivi numeri](https://docs.google.com/spreadsheet/ccc?key=0AvdkMlz2NopEdEo1Y2p2R0VvdnJvRXMwUVREbHRoLXc#gid=0), (i numeri hanno le virgole per leggibilità)
* [Washington, DC](https://docs.google.com/a/okfn.org/spreadsheet/ccc?key=0AvdkMlz2NopEdDhrZnRkWl9ZX2ZZNVptTzdueWw3emc#gid=0) (dati sulle transazioni)
* [Minsk, Belarus](https://docs.google.com/a/okfn.org/spreadsheet/ccc?key=0AvdkMlz2NopEdEtIMFlEVDZXOWdDUEthUTQ0c21aV2c#gid=0) (dati sui bilanci)
**Prossimo** [<a href="../pubblicare-i-dati-sul-web/">Pubblicare i dati sul web</a>]
**Precedente** [<a href="../raccolta-dati/">Raccolta Dati</a>]

View File

@ -1,36 +0,0 @@
---
section: help
lead: true
title: Guida ad OpenSpending
authors:
- stefanobandera
---
La guida ad OpenSpending è un manuale che copre l'intero processo di scoperta, raccolta e uso dai dati sulla piattaforma OpenSpending.
<strong>Introduzione</strong>
<ul>
<li><a href="it/cose-openspending/">Cos' è OpenSpending?</a></li>
<li><a href="it/quali-tipi-di-dati-finanziari-entrano-in-openspending/">Quali tipi di dati finanziari entrano in OpenSpending?</a></li>
<li><a href="it/come-vengono-rappresentati-i-dati-su-openspending/">Come vengono rappresentati i dati su OpenSpending?</a></li>
</ul>
<strong>Aggiungere dati su OpenSpending</strong>
<ul>
<li><a href="it/aggiungere-dati-ad-openspending/">Aggiugere dati</a></li>
<li><a href="it/raccolta-dati/">Raccolta dati</a></li>
<li><a href="it/formattare-i-dati/">Formattare i dati</a></li>
<li><a href="it/pubblicare-i-dati-sul-web/">Pubblicare i dati sul web</a></li>
<li><a href="it/creare-un-dataset-su-openspending/">Creare un dataset su OpenSpending</a></li>
<li><a href="it/modellare-i-tuoi-dati-su-openspending/">Modellare i tuoi dati su OpenSpending</a></li>
</ul>
<strong>Visualizzazioni</strong>
<ul>
<li><a href="it/crea-una-visualizzazione/">Crea una visualizzazione</a></li>
<li><a href="it/inserisci-una-visualizzazione-nel-tuo-sito-web/">Inserisci una visualizzazione nel tuo sito web</a></li>
</ul>
**Inizio** [<a href="it/cose-openspending/">Cos'è OpenSpending?</a>]
**Up** [ <a href="/help/guide/">Altre guide</a>]

View File

@ -1,56 +0,0 @@
---
section: help
lead: true
title: Inserisci una visualizzazione nel tuo sito web
authors:
- stefanobandera
---
Si può facilmente incorporare sul proprio sito web una delle visualizzazioni create su OpenSpending. Questo significa che puoi avere i display interattivi anche sul tuo sito.
Presupponiamo che abbiate scelto una visualizzazione su Open Spending. Se noti sulla parte in basso a destra della pagina c'è un pulsante *Embed*. Fai clic su questo pulsante e ti verrà presentato il codice per incorporare la visualizzazione sul vostro sito web e alcune opzioni per le dimensioni (in pixel) . Per il resto bisogna solo tagliare e incollare questo codice nel tuo sito. Se non siete sicuri su come incollare correttamente il codice, contattare l'amministratore del sito.
Il motivo per cui è possibile incorporare un codice dipende dai widget. In termini molto semplificati, un widget è un pezzo di codice che è possibile aggiungere alla tua pagina web, e tira i dati - in questo caso, dal database di OpenSpending - in modo che non sia necessario memorizzare i dataset per proprio conto.
**Siti Satelliti**
## INESC - Orçamento ao seu Alcançe (Budget alla tua portata?)
Questa è stata una collaborazione tra OKF Brasil _ INESC (Istituto di studi socio-economici), una ONG brasiliano. L'obiettivo era quello di rendere più facile per il pubblico monitorare il bilancio federale brasiliano, e come è suddiviso tra i molti enti pubblici, con una particolare attenzione sui capitoli di spesa.
I dati provengono da SIGA Brasil, un aggregatore dei tanti sistemi utilizzati dal governo per organizzare il bilancio. Questo ci permette di scegliere le colonne che vogliamo, ad esempio ente pubblico, categoria, sottocategoria, budget, spese, ecc, _ esportare in un file CSV. dati dal 2001, fino alla data attuale, aggiornato quotidianamente. A parte alcuni problemi, come le righe con mese "00", non abbiamo dovuto modificare molto per caricarlo in OpenSpending.
### Costruire il sito
Sapevamo di volerci concentrare su OpenSpending, cosa che ora avviene a vari livelli per tutti gli enti pubblici. Nel 2012, per esempio, il Ministero dell'Istruzione non ha speso il 16,3% del proprio bilancio (circa 6,1 miliardi di dollari). OpenSpending non aveva un grafico, out-of-the-box, che andrebbe bene per questo tipo di dati. Così abbiamo progettato il nostro software.
Dopo alcune ore di prove, abbiamo deciso di fare un grafico a serie cronologica, con barre e linee. Il grafico in figura X mostra i dati del 2012 del Ministero dell'educazione . L'area blu rappresenta il budget (nota che cambia nel corso dell'anno). Le barre mostrano quanto è stato speso in quel determinato mese, e la linea mostra il totale delle spese fino ad ora. Si può vedere, dalla distanza della linea rossa dalla punta dell'area blu, che a Dicembre è stato alquanto sotto utilizzato.
<img class="alignnone size-medium wp-image-1590" src="http://community.openspending.org/files/2013/10/image_20-300x147.png" alt="image_20" width="300" height="147" />
Per costruire questo grafico, stiamo usando NVD3, una libreria JavaScript con una raccolta di grafici riutilizzabili in D3. Il dato proviene da OpenSpending, utilizzando le API Aggregate. E 'ottimo e, dopo aver fatto un aggregazione, il risultato si memorizza nella cache e il programma diventa molto veloce. Ma esiste una limitazione che ci ha dato alcuni problemi: è possibile utilizzare solo una misura alla volta.
Per questo grafico, abbiamo 2 misure: il budget ed i pagamenti. Ma, internamente, i pagamenti sono divisi in due parti: quello che è stato pagato per l'anno in corso, ed i debiti pagati negli anni precedenti. Così, ci troviamo con 3 misure. Visto che l'API Aggregata ne consente solo una, abbiamo dovuto fare tre richieste per la costruzione di questo grafico.
Questo, ovviamente, crea un problema di prestazioni, sia per il nostro progetto sia per OpenSpending stesso. Ma, visto che le richieste vengono memorizzate nella cache dopo il primo utilizzo, si finisce per risolvere tutto. Esistono già piani per supportare più misure nelle API, quindi anche questo problema verrà risolto.
### Per usare il Treemap
Nella pagina di indice, abbiamo voluto mostrare una visione ampia del il bilancio riguardante gli enti pubblici. Inoltre, abbiamo voluto mostrare la quantità di denaro che viene utilizzato sia per funzione che per sottofunzione, come listruzione generale e l'istruzione di base. Per dimostrare questo, abbiamo scelto il Treemap.
Tramite un widget è stato facile: basta crearlo in OpenSpending, prendere il codice e incollarlo nel sito. Ma abbiamo però incontrato alcune limitazioni.
I widget sono fatti per quando si vuole semplicemente mettere il grafico in un post sul blog o articolo di giornale. Non è possibile personalizzarlo. Abbiamo dovuto cambiare i caratteri e i colori, per farlo entrare all'interno del design del resto della pagina. Dato che è un iframe, non c'è modo di cambiarlo usando solo CSS. Ma c'è una soluzione semplice: copiare il codice iframe del widget nella tua pagina.
<img class="alignnone size-medium wp-image-1591" src="http://community.openspending.org/files/2013/10/image_21-300x173.png" alt="image_21" width="300" height="173" />
Non c'è bisogno di costruire un treemap, bastano poche righe di codice di inizializzazione. Quando è nella tua pagina, è possibile utilizzare anche CSS. Purtroppo, questo non funziona per tutto: i colori non possono essere modificati in questo modo. Ma è facile configurare un altro schema di colori: devi semplicemente cambiare il codice di inizializzazione. Abbiamo anche aggiunto un pulsante "Indietro", in modo da poter navigare facilmente tra funzioni e sottofunzioni.
**Searching**
Per aiutare l'utente a trovare gli enti pubblici, abbiamo implementato un motore di ricerca con il completamento automatico, utilizzando il plug-in di Twitter Bootstrap typeahead. Poiché non ci sono molte entità (circa 500), abbiamo deciso di caricarle tutte quando l'utente entra prima nella pagina, in modo che la ricerca sia istantanea.
<img class="alignnone size-medium wp-image-1592" src="http://community.openspending.org/files/2013/10/image_22-300x177.png" alt="image_22" width="300" height="177" />
**Precedente** [<a href="../crea-una-visualizzazione/">Crea una visualizzazione</a>]
**Inizio** [<a href="{{site.baseurl}}/help/guide/it/">Guida ad OpenSpending</a>]

View File

@ -1,76 +0,0 @@
---
section: help
lead: true
title: Modellare i tuoi dati su OpenSpending
authors:
- stefanobandera
---
Per caricare i dati in OpenSpending, è necessario costruire un modello - dati. Un modello serve a specificare come i dati si traducano in termini che OpenSpending comprenda. OpenSpending rappresenta le proprietà dei dati in termini di dimensioni. I dati di modello si realizzano elencando le dimensioni che si desidera studiare nel dataset caricato su OpenSpending, e specificando come questi si relazionino alle colonne presenti nei dati di origine.
### Dimensioni obbligatorie: quantità e tempo
Ogni modello deve avere almeno due dimensioni: una quantità e un tempo. Questi specificano la dimensione della transazione e il momento in cui questa è avvenuta. La quantità e il tempo sono associati a particolari tipi di dimensioni. L'importo viene rappresentato da una misura, e il tempo è rappresentato da una data. Dimensioni generiche non possono rappresentare questi valori specifici.
Quando modelli i dati, non è una cattiva idea quella di iniziare con le dimensioni obbligatorie. Per iniziare, fai clic su *Dimensions &amp; Measures* all'interno della pagina **Manage the dataset**.
<img class="alignnone size-medium wp-image-1568" src="http://community.openspending.org/files/2013/09/image_5-300x113.png" alt="image_5" width="300" height="113" />
Successivamente, fai clic su *Add Dimension *per aprire il nuovo pannello *Add new dimension*. Clicca sul pulsante *Date*. Vedrai la casella *Name box *riempirsi automaticamente con il "tempo", come mostrato di seguito. Fai clic sul pulsante verde Aggiungi (**Add**).
<img class="alignnone size-medium wp-image-1569" src="http://community.openspending.org/files/2013/09/image_6-300x215.png" alt="image_6" width="300" height="215" />
La prossima schermata fornirà alcune informazioni sul significato del tempo. Nella casella a discesa accanto alla Colonna, seleziona la colonna dei dati che rappresenta il valore del tempo.
<img class="alignnone size-medium wp-image-1570" src="http://community.openspending.org/files/2013/09/image_7-300x129.png" alt="image_7" width="300" height="129" />
Dopo aver identificato la colonna tempo, fai clic su *Add Dimension *per aggiungere la dimensione quantità. Questa volta, selezionare il pulsante di opzione *Misura*, che riempirà automaticamente la colonna "amount", e clicca su *Add*. Scegli la colonna che rappresenta il valore della transazione tramite il box a discesa accanto alla Colonna.
### Le dimensioni chiavi e di compound
Soltanto una dimensione supplementare è necessaria per rendere il modello valido: la dimensione (o insieme di dimensioni) il cui valore identifica ciascun punto dei dati, la chiave.
Un punto dati non deve essere identificato dal valore di una singola colonna. Esso può essere identificato dalla combinazione di varie colonne in una dimensione composta. Poiché le chiavi possono essere composte, il tipo di dimensione deve essere utilizzato per rappresentarle, anche se la chiave particolare non è composta.
Per aggiungere la dimensione chiave, clicca su ** *Add Dimension * **e seleziona il pulsante *Dimension*. Inserisci un nome per la chiave, come "key", nel *Name box*. Fai clic su ** *Add* **. Selezionare la casella *include _ unique key *per identificare questa dimensione come parte della vostra chiave.
Successivamente, date un'occhiata alla **Field list**, che contiene due file *name *e *label*. Una dimensione composta può contenere un numero arbitrario di campi, ciascuno dei quali ha un nome e una tipizzazione e ciascuno dei quali può essere associato a una colonna nei dati. Questo è il senso per cui tali dimensioni sono dette "composte": esse raggruppano colonne multiple derivanti dai dati di origine, in una singola proprietà del dataset di destinazione.
<img class="alignnone size-medium wp-image-1571" src="http://community.openspending.org/files/2013/09/image_8-300x172.png" alt="image_8" width="300" height="172" />
Una dimensione composta richiede almeno due campi, *name* e *label*, che devono essere rispettivamente di tipo id e di stringa. Il nome della dimensione viene utilizzato per dotarla di un URL funzionante, l'etichetta viene utilizzata per presentarla nell'interfaccia utente.
Per creare una dimensione composta minima, è sufficiente associare la stessa colonna dei dati di origine con nome ed etichetta. Scegliere la colonna appropriata per ciascuna dimensione e lasciare le tipologie predefiniti invariati.
### Misure e altre dimensioni
Con quantità, tempo e chiave, il modello è sufficientemente ricco. Un modello veramente completo, tuttavia, comprenderà dimensioni per ogni proprietà significativa dei dati di origine. Seguendo alcune convenzioni questo diventa più conveniente.
Un modello comune per i dati di origine sta diffondendo informazioni che identificano entità - gruppi, account, e così via - tramite diverse colonne. Le informazioni su un account associato a una transazione ad esempio possono essere visualizzate dividendo in una colonna "Account", con un numero di identificazione e una colonna "descrizione Account" con una descrizione verbale. "Head-account" e "Sub-account" nell'immagine qui di seguito presentano questo modello.
<img class="alignnone size-medium wp-image-1572" src="http://community.openspending.org/files/2013/09/image_9-300x226.png" alt="image_9" width="300" height="226" />
Le dimensioni composte di OpenSpending sono progettate per modellare questo tipo di informazioni sparse. Per farlo, aggiungi una nuova dimensione composta e associa ogni colonna ad uno dei campi della dimensione creata. Cerca di far corrispondere una colonna leggibile e una colonna più concisa per essere rinominata. Nell'immagine sottostante, "Head-account" è abbinato a *name* e " Head-account description" a *label*.
<img class="alignnone size-medium wp-image-1573" src="http://community.openspending.org/files/2013/09/image_10-300x185.png" alt="image_10" width="300" height="185" />
Alcune colonne di dati sono più autonome, rappresentando particolari attributi di ciascun punto dati. Una colonna che ordina ogni transazione in qualche categoria, per esempio, è di questo tipo. Nell'immagine sottostante le colonne Reporting Type, Revenue/Expenditure, e Recurrent/Investment sono di questo tipo.
<img class="alignnone size-medium wp-image-1574" src="http://community.openspending.org/files/2013/09/image_11-300x186.png" alt="image_11" width="300" height="186" />
Colonne indipendenti che specificano attributi o categorie sono meglio modellate con le dimensioni *attribute*. Un attributo è essenzialmente una dimensione con un solo campo, che può essere di qualsiasi tipo. Per creare un attributo, è sufficiente selezionare il pulsante di opzione *Attribute* quando si aggiunge una dimensione.
<img class="alignnone size-medium wp-image-1575" src="http://community.openspending.org/files/2013/09/image_12-300x236.png" alt="image_12" width="300" height="236" />
**Concludendo: salvataggio e caricamento**
Quando ogni dimensioni è stato impostata e collegata alle rispettive colonne nei dati di origine, fai clic su *Save Dimensions* per salvare il modello. Se qualcosa è sbagliato con il modello, viene visualizzato un messaggio di errore che richiede di correggere i parametri. In caso contrario, verrà visualizzato un messaggio che ti invita a tornare alla dashboard, dove si può procedere a caricare i dati.
Una volta che i dati sono stati caricati, il modello creato sarà fisso e il montaggio sarà disattivato. Quindi,volendo, si può testare il modello prima di caricarlo. Per fare questo, clicca su Test nella riga dedicata alla fonte dati presente nella dashboard. Attendi alcuni secondi, quindi ricarica la pagina. Se viene visualizzato un messaggio con uno sfondo verde che dice COMPLETE, il modello è pronto a partire. Se vedi errori, sono necessarie alcune riparazioni.
<img class="alignnone size-medium wp-image-1576" src="http://community.openspending.org/files/2013/09/image_13-300x139.png" alt="image_13" width="300" height="139" />
Se il vostro modello è privo di errori, fai clic su *Load* per caricare il dataset di origine e applicare il modello. Si può quindi tornare alla home page del dataset cliccando sul suo nome nella parte superiore della schermata, in cui è possibile procedere alla costruzione di effetti grafici e giocare con i vostri dati.
**Prossimo** [<a href="../crea-una-visualizzazione/">Crea una visualizzazione</a>]
**Precedente** [<a href="../creare-un-dataset-su-openspending/">Creare un dataset su OpenSpending</a>]

View File

@ -1,38 +0,0 @@
---
section: help
lead: true
title: Pubblicare i dati sul web
authors:
- stefanobandera
---
I dati non possono (ancora) essere caricati direttamente su OpenSpending. Per essere aggiunti al database OpenSpending, i dati devono prima essere resi accessibili dal web. Questa sezione presenta due modi convenienti per pubblicare il dataset on-line.
**Google Drive**
È possibile rendere i dati accessibili sul web, trasformandoli in un foglio di calcolo di Google Drive.
1. Importa i tuoi dati. Crea un nuovo foglio di calcolo di Google Drive, quindi seleziona Import ... dal menu File. Selezionare *Replace Spreadsheet*, fai clic su **Choose file**, e sposta il file CSV.
2. Assicurati che Google Docs non sfalsi le date dei vostri dati. Seleziona la colonna che contiene le date. Fai clic sul menu *Format* e selezionare *Number* -&gt; *More formats *-&gt; 2008-09-26. I dati dovrebbero apparire nel formato **aaaa-mm-gg **.
3. Fai clic sul menu File e seleziona *Publish to the web *.... Nella finestra che appare, clic sul pulsante ** *Start publishing* **. Sotto *get a link to the published data , select *** *CSV* *** *(comma separated values).
<img class="alignnone size-medium wp-image-1561" src="http://community.openspending.org/files/2013/09/image_0-300x290.png" alt="image_0" width="300" height="290" />
LURL in fondo al box ora si riferisce ai tuoi dati.
**Gist**
GitHub Gist è un modo conveniente per ospitare piccole quantità di testo, inclusi i file CSV.
1. Fai log-in su GitHub (o registrati se non l'hai già fatto), quindi naviga su [gist.github.com](https://gist.github.com/).
2. Fai clic e trascina il file CSV dal file manager del tuo sistema operativo alla pagina di Sintesi *GitHub *del tuo browser. Verranno visualizzati il nome e il contenuto del file.
3. Fare clic su Create Public Gist per essere trasportati nella homepage del vostro nuovo Gist. L'URL relativo ai tuoi dati è accessibile attraverso il pulsante "angle brackets (parentesi angolari)" nell'angolo in alto a destra del file.
<img class="alignnone size-medium wp-image-1562" src="http://community.openspending.org/files/2013/09/image_1-300x71.png" alt="image_1" width="300" height="71" />
**Prossimo** [<a href="../creare-un-dataset-su-openspending/">Creare un dataset su OpenSpending</a>]
**Precedente** [<a href="../formattare-i-dati/">Formattare i dati</a>]

View File

@ -1,48 +0,0 @@
---
section: help
lead: true
title: Quali tipi di dati finanziari entrano in OpenSpending?
authors:
- stefanobandera
---
OpenSpending è molto flessibile nel tipo di dati finanziari che supporta. Anche se il progetto OpenSpending ha un forte focus sulla finanza pubblica, questo non è un vincolo tecnico. OpenSpending supporta qualsiasi insieme di dati costituito da un insieme di transazioni, ciascuna associata ad una quantità di denaro e un periodo di tempo.
La maggior parte dei dati attualmente ospitati su OpenSpending possono essere classificati come dati transazionali o di bilancio. La differenza principale tra questi è il loro livello di granularità. Dati transazionali o di transazione tracciano singole operazioni, mentre i dati sul bilancio aggregano le transazioni in categorie.
### Dati di spesa derivanti da transazioni
I dati di transazione, o semplicemente "i dati di spesa", tracciano singole operazioni finanziarie. Ogni pagamento tra singoli soggetti, in una determinata data, e per uno scopo specifico (ad esempio, un progetto o un servizio) è elencato singolarmente. Dati di spesa transazionali comprendono vari tipi di record, comprese le informazioni sui contributi pubblici, le obbligazioni, e le spese effettive.
Le informazioni aggregate (ad esempio la somma) non dovrebbero essere incluse nei dati di transazione. Dati che sono stati parzialmente o completamente aggregati richiedono una diversa modalità di analisi e devono essere trattati come dati di bilancio piuttosto che dati di transazione. Ciò non significa, tuttavia, che i diversi pagamenti "fisici", che si riferiscono ad una singola transazione "logica" non possano essere rappresentati da una singola operazione nella tipologia dei dati transazionali.
I dati di transazione in OpenSpending includono:
* [DC Venditori e Contraenti](http://openspending.org/dc-vendors-contractors)
* [LAustrian Development Agency](http://openspending.org/ada)
Un altro tipo correlato di dati si riferisce alle procedure di aggiudicazione degli appalti pubblici. Dati sugli appalti sono dati relativi a gare pubbliche: quanto è stato offerto, per quanto, e chi ha vinto la gara. Esso può essere visto come un sottoinsieme dei dati di transazione.
I dati sugli appalti comprendono:
* [Marchés publics au Sénégal](http://openspending.org/marches-publics-senegal/views/liste-des-attributaires)
* [Marchés publics France 2011](http://openspending.org/marches-publics-france-2011)
### Dati di bilancio
Nei dati di bilancio, le spese e i redditi sono aggregati per categorie. L'obiettivo di questa aggregazione è quello di aiutare il lettore nella comprensione del bilancio, che in genere è un documento di policy che viene utilizzato per fornire una panoramica sulle più importanti scelte finanziarie del governo. L'allocazione dei dati è tipicamente strutturata da uno schema di classificazione, piuttosto che tramite i destinatari effettivi dei fondi.
I dati di bilancio spesso presentano congiuntamente dati sui risultati del passato e sugli stanziamenti per un periodo futuro. In una tale presentazione, gli importi spesi negli anni precedenti su un particolare settore, sono utilizzati per informare su quanto dovrebbe essere assegnato per il prossimo periodo di programmazione finanziaria. Le informazioni di bilancio spesso si basano su stime e dati aggregati.
Diverse regioni riportano differenti tipi di informazioni di bilancio disponibili, tra cui: Dichiarazioni Pre-Budget, proposte di bilancio esecutivo, bilanci promulgati, e fondi stanziati per i cittadini (versioni semplificate di quote di bilancio a beneficio dei cittadini).
I dati di bilancio su OpenSpending includono:
* [Berlin Budget](http://openspending.org/berlin_de)
* [Seville Spending Budget](http://openspending.org/seville-budget)
**Prossimo** [<a href="../come-vengono-rappresentati-i-dati-su-openspending/">Come vengono rappresentati i dati su OpenSpending?</a>]
**Precedente** [<a href="../cose-openspending/">Cos'è OpenSpending</a>]

View File

@ -1,16 +0,0 @@
---
section: help
lead: true
title: Raccolta Dati
authors:
- stefanobandera
---
Per aggiungere un dataset ad OpenSpending, è necessario disporre di alcuni dati. Se già li avete, si può procedere. In caso contrario, è necessario trovarli.
Inizia la tua ricerca dati consultando alcune risorse, come la [School of Data](http://schoolofdata.org/handbook/courses/finding-data/) e il [Data Journalism Handbook](http://datajournalismhandbook.org/1.0/en/getting_data.html). È inoltre possibile ottenere alcune idee su come strutturare la ricerca visitando l [OpenSpending group](http://datahub.io/group/openspending) su datahub.io, e porre domande sul canale IRC # openspending su Freenode.
I dati che troverete, si spera siano in un formato "leggibile", per esempio sotto forma di un foglio di calcolo di Excel o in un file CSV. Se trovate i dati in un formato come PDF o un documento Word, questo sarà molto difficile da lavorare, e si potrebbe considerare semplicemente di cercare dati diversi!
**Prossimo** [<a href="../formattare-i-dati/">Formattare i dati</a>]
**Precedente** [<a href="../aggiungere-dati-ad-openspending/">Aggiungere dati ad OpenSpending</a>]

View File

@ -1,10 +0,0 @@
---
section: help
lead: true
title: Ghidul utilizatorului OpenSpending
authors:
- Anders Pedersen
---
<a href="https://docs.google.com/a/okfn.org/document/d/1GQuYU9QtYvClaKesrD1et5Rg6i7nXh35zzvMxDJTp5E/edit#">Ghidul utilizatorului OpenSpending</a> (as a Google Doc).
The guide needs to be reformatted and posted here (in markdown). You can help editing this guide page by getting in touch with <a href="https://trello.com/c/abfAVgBC/14-permanent-openspending-guide-translation">the community here</a>.

View File

@ -1,10 +0,0 @@
---
section: help
lead: true
title: AçıkHarcama Rehberi - The OpenSpending guide in Turkish
authors:
- Anders Pedersen
---
ıkHarcama Rehberi - the OpenSpending guide is available in Turkish as a short version in<strong> <a href="https://docs.google.com/a/okfn.org/document/d/1UWbheZpMs9_lJySXEQpQC6M4OeXPnswJ6c28-ALbIX0/edit#">this Google Doc</a></strong>.
You can help translate the rest of the OpenSpending guide into Turkish by accessing the <a href="https://docs.google.com/a/okfn.org/document/d/1-RhyBc7rFgBW78160BA0mxD6cpVZ-PtfM8QL_WEoPqY/edit#heading=h.79vyyu8n203g">original English language version</a>.

View File

@ -11,12 +11,12 @@ of our guides and get rolling! If you have questions,
The OpenSpending Guide is available in multiple languages:
* [English](./help/guide/en)
* [French](./help/guide/fr)
* [Italian](./help/guide/it)
* [Nepali](./help/guide/npl)
* [Romanian](./help/guide/rom)
* [Spanish](./help/guide/esp)
* [English](./guide/en)
* [French](./guide/fr)
* [Italian](./guide/it)
* [Nepali](./guide/npl)
* [Romanian](./guide/rom)
* [Spanish](./guide/esp)
Many other translations are under development. Check back for new
translations or

View File

@ -127,4 +127,4 @@ Based on the bar chart above we can conclude that the following 3 countries have
2. Poland - EUR ~68b.
3. Italy - EUR ~35b.
_This data story was created by using Datopian's PortalJS framework. You can learn more about the framework by visiting https://portaljs.com/_
_This data story was created by using Datopian's PortalJS framework. You can learn more about the framework by visiting https://portaljs.org/_

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@
"test": "vitest"
},
"dependencies": {
"@flowershow/markdowndb": "^0.1.8",
"@githubocto/flat-ui": "^0.14.1",
"@headlessui/react": "^1.7.14",
"@heroicons/react": "^2.0.18",
@ -35,7 +36,7 @@
"eslint-config-next": "13.4.3",
"flexsearch": "0.7.21",
"isomorphic-unfetch": "^4.0.2",
"mddb": "0.2.1",
"mddb": "^0.1.9",
"next": "13.4.3",
"next-mdx-remote": "^4.4.1",
"next-seo": "^6.0.0",

View File

@ -1,6 +1,6 @@
This demo data portal is designed for https://hatespeechdata.com. It catalogs datasets annotated for hate speech, online abuse, and offensive language which are useful for training a natural language processing system to detect this online abuse.
The site is built on top of [PortalJS](https://portaljs.com/). It catalogs datasets and lists of offensive keywords. It also includes static pages. All of these are stored as markdown files inside the `content` folder.
The site is built on top of [PortalJS](https://portaljs.org/). It catalogs datasets and lists of offensive keywords. It also includes static pages. All of these are stored as markdown files inside the `content` folder.
- .md files inside `content/datasets/` will appear on the dataset list section of the homepage and be searchable as well as having a individual page in `datasets/<file name>`
- .md files inside `content/keywords/` will appear on the list of offensive keywords section of the homepage as well as having a individual page in `keywords/<file name>`

View File

@ -21,7 +21,7 @@ export function Footer() {
<Container.Inner>
<div className="flex flex-col items-center justify-between gap-6 sm:flex-row">
<p className="text-sm font-medium text-zinc-800 dark:text-zinc-200">
Built with <a href='https://portaljs.com'>PortalJS 🌀</a>
Built with <a href='https://portaljs.org'>PortalJS 🌀</a>
</p>
<p className="text-sm text-zinc-400 dark:text-zinc-500">
&copy; {new Date().getFullYear()} Leon Derczynski. All rights

5088
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
"name": "@portaljs/ckan",
"version": "0.1.0",
"type": "module",
"description": "https://portaljs.com",
"description": "https://portaljs.org",
"keywords": [
"data portal",
"data catalog",

View File

@ -1,16 +1,9 @@
import 'tailwindcss/tailwind.css'
import '../src/index.css'
import type { Preview } from '@storybook/react';
window.process = {
...window.process,
env:{
...window.process?.env,
}
};
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },

View File

@ -1,127 +1,5 @@
# @portaljs/components
## 1.2.3
### Patch Changes
- [`62dbc35d`](https://github.com/datopian/portaljs/commit/62dbc35d3b39ea7409949340214ca83a448ee999) Thanks [@olayway](https://github.com/olayway)! - LineChart: break lines at invalid / missing values (don't connect if there are gaps in values).
## 1.2.2
### Patch Changes
- [`eeb480e8`](https://github.com/datopian/datahub/commit/eeb480e8cff2d11072ace55ad683a65f54f5d07a) Thanks [@olayway](https://github.com/olayway)! - Adjust `xAxisTimeUnit` property in LineChart to allow for passing `yearmonth`.
## 1.2.1
### Patch Changes
- [`836b143a`](https://github.com/datopian/datahub/commit/836b143a3178b893b1aae3fb511d795dd3a63545) Thanks [@olayway](https://github.com/olayway)! - Fix: make tileLayerName in Map optional.
## 1.2.0
### Minor Changes
- [#1338](https://github.com/datopian/datahub/pull/1338) [`63d9e3b7`](https://github.com/datopian/datahub/commit/63d9e3b7543c38154e6989ef1cc1d694ae9fc4f8) Thanks [@olayway](https://github.com/olayway)! - Support for plotting multiple series in LineChart component.
## 1.1.0
### Minor Changes
- [#1122](https://github.com/datopian/datahub/pull/1122) [`8e349678`](https://github.com/datopian/datahub/commit/8e3496782c022b0653e07f217c6b315ba84e0e61) Thanks [@willy1989cv](https://github.com/willy1989cv)! - Map: allow users to choose a base layer setting
## 1.0.1
### Patch Changes
- [#1170](https://github.com/datopian/datahub/pull/1170) [`9ff25ed7`](https://github.com/datopian/datahub/commit/9ff25ed7c47c8c02cc078c64f76ae35d6754c508) Thanks [@lucasmbispo](https://github.com/lucasmbispo)! - iFrame component: change height
## 1.0.0
### Major Changes
- [#1103](https://github.com/datopian/datahub/pull/1103) [`48cd812a`](https://github.com/datopian/datahub/commit/48cd812a488a069a419d8ecc67f24f94d4d1d1d6) Thanks [@demenech](https://github.com/demenech)! - Components API tidying up and storybook docs improvements.
## 0.6.0
### Minor Changes
- [`a044f56e`](https://github.com/datopian/portaljs/commit/a044f56e3cbe0519ddf9d24d78b0bb7eac917e1c) Thanks [@luccasmmg](https://github.com/luccasmmg)! - Added plotly components
## 0.5.10
### Patch Changes
- [#1083](https://github.com/datopian/portaljs/pull/1083) [`86a2945e`](https://github.com/datopian/portaljs/commit/86a2945ee68dfcea0299984ca9cc9070d68fe1c2) Thanks [@Gutts-n](https://github.com/Gutts-n)! - Created integration with datastore api for table component
## 0.5.9
### Patch Changes
- [#1081](https://github.com/datopian/portaljs/pull/1081) [`2bbf3134`](https://github.com/datopian/portaljs/commit/2bbf3134896df3ecc66560bdf95bece143614c7b) Thanks [@Gutts-n](https://github.com/Gutts-n)! - Fixed error to remove anchor from document
## 0.5.8
### Patch Changes
- [#1079](https://github.com/datopian/portaljs/pull/1079) [`058d2367`](https://github.com/datopian/portaljs/commit/058d23678a024890f8a6d909ded9fc8fc11cf145) Thanks [@Gutts-n](https://github.com/Gutts-n)! - Changed the download behaviour of the bucket viewer component and removed loading component while downloading
## 0.5.7
### Patch Changes
- [#1077](https://github.com/datopian/portaljs/pull/1077) [`6d7acd27`](https://github.com/datopian/portaljs/commit/6d7acd27ed9299cbcc14eab906f2f0eb414656b8) Thanks [@Gutts-n](https://github.com/Gutts-n)! - Created property to present a component while is loading the download of the file and fixed download bug on pagination
## 0.5.6
### Patch Changes
- [#1075](https://github.com/datopian/portaljs/pull/1075) [`26dcffc2`](https://github.com/datopian/portaljs/commit/26dcffc279057f80a579134e862085ba042c06c3) Thanks [@Gutts-n](https://github.com/Gutts-n)! - Fixed problem presenting the download component in the first load of the bucket viewer
## 0.5.5
### Patch Changes
- [#1073](https://github.com/datopian/portaljs/pull/1073) [`cf24042a`](https://github.com/datopian/portaljs/commit/cf24042a910567e98eeb75ade42ce0149bdb62d1) Thanks [@Gutts-n](https://github.com/Gutts-n)! - Fixed filter by startDate error
## 0.5.4
### Patch Changes
- [#1071](https://github.com/datopian/portaljs/pull/1071) [`27c99add`](https://github.com/datopian/portaljs/commit/27c99adde8fa36ad2c2e03f227f93aa62454eefa) Thanks [@Gutts-n](https://github.com/Gutts-n)! - Added pagination and filter properties for the BucketViewer component
## 0.5.3
### Patch Changes
- [#1066](https://github.com/datopian/portaljs/pull/1066) [`dd03a493`](https://github.com/datopian/portaljs/commit/dd03a493beca5459d1ef447b2df505609fc64e95) Thanks [@Gutts-n](https://github.com/Gutts-n)! - Created Iframe component
## 0.5.2
### Patch Changes
- [#1063](https://github.com/datopian/portaljs/pull/1063) [`b13e3ade`](https://github.com/datopian/portaljs/commit/b13e3ade3ccefe7dffe84f824bdedd3e512ce499) Thanks [@Gutts-n](https://github.com/Gutts-n)! - Created auto zoom configuration for the map component
## 0.5.1
### Patch Changes
- [#1061](https://github.com/datopian/portaljs/pull/1061) [`4ddfc112`](https://github.com/datopian/portaljs/commit/4ddfc1126a3f0b8137ea47a08a36c56b7373b8f6) Thanks [@Gutts-n](https://github.com/Gutts-n)! - Created the style property in the Map component
## 0.5.0
### Minor Changes
- [#1055](https://github.com/datopian/portaljs/pull/1055) [`712f4a3b`](https://github.com/datopian/portaljs/commit/712f4a3b0f074e654879bb75059f51e06b422b32) Thanks [@Gutts-n](https://github.com/Gutts-n)! - Creation of BucketViewer component to show the data of public buckets
- [#1057](https://github.com/datopian/portaljs/pull/1057) [`61c750b7`](https://github.com/datopian/portaljs/commit/61c750b7e11fe52bf04d25f192440ee1bb307404) Thanks [@Gutts-n](https://github.com/Gutts-n)! - Exporting BucketViewer to be accessed out of the folder
## 0.4.0
### Minor Changes
- [#1022](https://github.com/datopian/portaljs/pull/1022) [`83fd7727`](https://github.com/datopian/portaljs/commit/83fd7727bafb4902218777597e9848a3e3a71d87) Thanks [@luccasmmg](https://github.com/luccasmmg)! - FlatUiTables now accepts a bytes param and a parsingConfig param for CSV links
## 0.3.2
### Patch Changes

View File

@ -1,7 +1,7 @@
# PortalJS React Components
**Storybook:** https://storybook.portaljs.org
**Docs**: https://portaljs.com/opensource
**Docs**: https://portaljs.org/docs
## Usage

View File

@ -1,8 +1,8 @@
{
"name": "@portaljs/components",
"version": "1.2.3",
"version": "0.3.2",
"type": "module",
"description": "https://portaljs.com",
"description": "https://portaljs.org",
"keywords": [
"data portal",
"data catalog",
@ -29,8 +29,6 @@
"@githubocto/flat-ui": "^0.14.1",
"@heroicons/react": "^2.0.17",
"@planet/maps": "^8.1.0",
"@react-pdf-viewer/core": "3.6.0",
"@react-pdf-viewer/default-layout": "3.6.0",
"@tanstack/react-table": "^8.8.5",
"ag-grid-react": "^30.0.4",
"chroma-js": "^2.4.2",
@ -39,19 +37,19 @@
"next-mdx-remote": "^4.4.1",
"ol": "^7.4.0",
"papaparse": "^5.4.1",
"pdfjs-dist": "2.15.349",
"plotly.js": "^2.30.1",
"postcss-url": "^10.1.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.43.9",
"react-leaflet": "^4.2.1",
"react-plotly.js": "^2.6.0",
"react-query": "^3.39.3",
"react-vega": "^7.6.0",
"vega": "5.25.0",
"vega-lite": "5.1.0",
"vitest": "^0.31.4",
"@react-pdf-viewer/core": "3.6.0",
"@react-pdf-viewer/default-layout": "3.6.0",
"pdfjs-dist": "2.15.349",
"xlsx": "^0.18.5"
},
"devDependencies": {

View File

@ -1,222 +0,0 @@
import { CSSProperties, ReactNode, useEffect, useState } from 'react';
import LoadingSpinner from './LoadingSpinner';
export interface BucketViewerFilterSearchedDataEvent {
startDate?: Date;
endDate?: Date;
}
export interface BucketViewerProps {
onLoadTotalNumberOfItems?: (total: number) => void;
domain: string;
downloadConfig?: {
hoverOfTheFileComponent?: ReactNode;
};
suffix?: string;
className?: string;
paginationConfig?: BucketViewerPaginationConfig;
filterState?: BucketViewerFilterSearchedDataEvent;
dataMapperFn: (rawData: Response) => Promise<BucketViewerData[]>;
}
export interface BucketViewerPaginationConfig {
containerClassName?: string;
containerStyles?: CSSProperties;
itemsPerPage: number;
}
export interface BucketViewerData {
fileName: string;
downloadFileUri: string;
dateProps?: {
date: Date;
dateFormatter?: (date: Date) => string;
};
}
export function BucketViewer({
domain,
suffix,
dataMapperFn,
className,
filterState,
paginationConfig,
downloadConfig,
onLoadTotalNumberOfItems,
}: BucketViewerProps) {
suffix = suffix ?? '/';
const { hoverOfTheFileComponent } = downloadConfig ?? {};
const [isLoading, setIsLoading] = useState<boolean>(false);
const [showDownloadComponentOnLine, setShowDownloadComponentOnLine] =
useState(-1);
const [currentPage, setCurrentPage] = useState<number>(0);
const [lastPage, setLastPage] = useState<number>(0);
const [bucketFiles, setBucketFiles] = useState<BucketViewerData[]>([]);
const [paginatedData, setPaginatedData] = useState<BucketViewerData[]>([]);
const [filteredData, setFilteredData] = useState<BucketViewerData[]>([]);
useEffect(() => {
setIsLoading(true);
fetch(`${domain}${suffix}`)
.then((res) => dataMapperFn(res))
.then((data) => {
setBucketFiles(data);
setFilteredData(data);
})
.finally(() => setIsLoading(false));
}, [domain, suffix]);
useEffect(() => {
if (paginationConfig) {
const startIndex = paginationConfig
? currentPage * paginationConfig.itemsPerPage
: 0;
const endIndex = paginationConfig
? startIndex + paginationConfig.itemsPerPage
: 0;
setLastPage(
Math.ceil(filteredData.length / paginationConfig.itemsPerPage) - 1
);
setPaginatedData(filteredData.slice(startIndex, endIndex));
}
}, [currentPage, filteredData]);
useEffect(() => {
if (onLoadTotalNumberOfItems) onLoadTotalNumberOfItems(filteredData.length);
}, [filteredData]);
useEffect(() => {
if (!filterState) return;
if (filterState.startDate && filterState.endDate) {
setFilteredData(
bucketFiles.filter(({ dateProps }) =>
dateProps
? dateProps.date.getTime() >= filterState.startDate.getTime() &&
dateProps.date.getTime() <= filterState.endDate.getTime()
: true
)
);
} else if (filterState.startDate) {
setFilteredData(
bucketFiles.filter(({ dateProps }) =>
dateProps
? dateProps.date.getTime() >= filterState.startDate.getTime()
: true
)
);
} else if (filterState.endDate) {
setFilteredData(
bucketFiles.filter(({ dateProps }) =>
dateProps
? dateProps.date.getTime() <= filterState.endDate.getTime()
: true
)
);
} else {
setFilteredData(bucketFiles);
}
}, [filterState]);
return isLoading ? (
<div className="w-full flex items-center justify-center h-[300px]">
<LoadingSpinner />
</div>
) : bucketFiles ? (
<>
{...(paginationConfig && bucketFiles ? paginatedData : filteredData)?.map(
(data, i) => (
<ul
onClick={() => {
const a: HTMLAnchorElement = document.createElement('a');
a.href = data.downloadFileUri;
a.target = `_blank`;
a.download = data.fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}}
key={i}
onMouseEnter={() => setShowDownloadComponentOnLine(i)}
onMouseLeave={() => setShowDownloadComponentOnLine(undefined)}
className={`${
className ??
'mb-2 border-b-[2px] border-b-[red] hover:cursor-pointer'
}`}
>
{hoverOfTheFileComponent && showDownloadComponentOnLine === i ? (
hoverOfTheFileComponent
) : (
<></>
)}
<div className="flex justify-between w-full items-center">
<div>
<li>{data.fileName}</li>
{data.dateProps && data.dateProps.dateFormatter ? (
<li>{data.dateProps.dateFormatter(data.dateProps.date)}</li>
) : (
<></>
)}
</div>
</div>
</ul>
)
)}
{paginationConfig ? (
<ul
className={
paginationConfig.containerClassName
? paginationConfig.containerClassName
: 'flex justify-end gap-x-[0.5rem] w-full'
}
style={paginationConfig.containerStyles ?? {}}
>
<li>
<button
className="hover:cursor-pointer hover:disabled:cursor-not-allowed"
disabled={currentPage === 0}
onClick={() => setCurrentPage(0)}
>
First
</button>
</li>
<li>
<button
className="hover:cursor-pointer hover:disabled:cursor-not-allowed"
onClick={() => setCurrentPage(currentPage - 1)}
disabled={currentPage === 0}
>
Previous
</button>
</li>
<label>{currentPage + 1}</label>
<li>
<button
onClick={() => setCurrentPage(currentPage + 1)}
disabled={currentPage >= lastPage}
className="hover:cursor-pointer hover:disabled:cursor-not-allowed"
>
Next
</button>
</li>
<li>
<button
onClick={() => setCurrentPage(lastPage)}
disabled={currentPage >= lastPage}
className="hover:cursor-pointer hover:disabled:cursor-not-allowed"
>
Last
</button>
</li>
</ul>
) : (
<></>
)}
</>
) : null;
}

View File

@ -7,12 +7,7 @@ export function Catalog({
datasets,
facets,
}: {
datasets: {
_id: string | number;
metadata: { title: string; [k: string]: string | number };
url_path: string;
[k: string]: any;
}[];
datasets: any[];
facets: string[];
}) {
const [indexFilter, setIndexFilter] = useState('');
@ -61,7 +56,7 @@ export function Catalog({
//Then check if the selectedValue for the given facet is included in the dataset metadata
.filter((dataset) => {
//Avoids a server rendering breakage
if (!watch() || Object.keys(watch()).length === 0) return true;
if (!watch() || Object.keys(watch()).length === 0) return true
//This will filter only the key pairs of the metadata values that were selected as facets
const datasetFacets = Object.entries(dataset.metadata).filter((entry) =>
facets.includes(entry[0])
@ -91,7 +86,9 @@ export function Catalog({
className="p-2 ml-1 text-sm shadow border border-block"
{...register(elem[0] + '.selectedValue')}
>
<option value="">Filter by {elem[0]}</option>
<option value="">
Filter by {elem[0]}
</option>
{(elem[1] as { possibleValues: string[] }).possibleValues.map(
(val) => (
<option
@ -105,10 +102,10 @@ export function Catalog({
)}
</select>
))}
<ul className="mb-5 pl-6 mt-5 list-disc">
<ul className='mb-5 pl-6 mt-5 list-disc'>
{filteredDatasets.map((dataset) => (
<li className="py-2" key={dataset._id}>
<a className="font-medium underline" href={dataset.url_path}>
<li className='py-2' key={dataset._id}>
<a className='font-medium underline' href={dataset.url_path}>
{dataset.metadata.title
? dataset.metadata.title
: dataset.url_path}
@ -119,3 +116,4 @@ export function Catalog({
</>
);
}

View File

@ -4,14 +4,12 @@ import { read, utils } from 'xlsx';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import { Data } from '../types/properties';
export type ExcelProps = {
data: Required<Pick<Data, 'url'>>;
url: string;
};
export function Excel({ data }: ExcelProps) {
const url = data.url;
export function Excel({ url }: ExcelProps) {
const [isLoading, setIsLoading] = useState<boolean>(false);
const [activeSheetName, setActiveSheetName] = useState<string>();
const [workbook, setWorkbook] = useState<any>();

View File

@ -2,7 +2,6 @@ import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
import Papa from 'papaparse';
import { Grid } from '@githubocto/flat-ui';
import LoadingSpinner from './LoadingSpinner';
import { Data } from '../types/properties';
const queryClient = new QueryClient();
@ -37,25 +36,30 @@ export async function parseCsv(file: string, parsingConfig): Promise<any> {
}
export interface FlatUiTableProps {
data: Data;
uniqueId?: number;
url?: string;
data?: { [key: string]: number | string }[];
rawCsv?: string;
randomId?: number;
bytes: number;
parsingConfig: any;
}
export const FlatUiTable: React.FC<FlatUiTableProps> = ({
url,
data,
uniqueId,
rawCsv,
bytes = 5132288,
parsingConfig = {},
}) => {
uniqueId = uniqueId ?? Math.random();
const randomId = Math.random();
return (
// Provide the client to your App
<QueryClientProvider client={queryClient}>
<TableInner
bytes={bytes}
url={url}
data={data}
uniqueId={uniqueId}
rawCsv={rawCsv}
randomId={randomId}
parsingConfig={parsingConfig}
/>
</QueryClientProvider>
@ -63,32 +67,33 @@ export const FlatUiTable: React.FC<FlatUiTableProps> = ({
};
const TableInner: React.FC<FlatUiTableProps> = ({
url,
data,
uniqueId,
rawCsv,
randomId,
bytes,
parsingConfig,
}) => {
const url = data.url;
const csv = data.csv;
const values = data.values;
if (values) {
if (data) {
return (
<div className="w-full" style={{ height: '500px' }}>
<Grid data={values} />
<Grid data={data} />
</div>
);
}
const { data: csvString, isLoading: isDownloadingCSV } = useQuery(
['dataCsv', url, uniqueId],
['dataCsv', url, randomId],
() => getCsv(url as string, bytes),
{ enabled: !!url }
);
const { data: parsedData, isLoading: isParsing } = useQuery(
['dataPreview', csvString, uniqueId],
['dataPreview', csvString, randomId],
() =>
parseCsv(csv ? (csv as string) : (csvString as string), parsingConfig),
{ enabled: csv ? true : !!csvString }
parseCsv(
rawCsv ? (rawCsv as string) : (csvString as string),
parsingConfig
),
{ enabled: rawCsv ? true : !!csvString }
);
if (isParsing || isDownloadingCSV)
<div className="w-full flex justify-center items-center h-[500px]">

View File

@ -1,17 +0,0 @@
import { CSSProperties } from 'react';
import { Data } from '../types/properties';
export interface IframeProps {
data: Required<Pick<Data, 'url'>>;
style?: CSSProperties;
}
export function Iframe({ data, style }: IframeProps) {
const url = data.url;
return (
<iframe
src={url}
style={style ?? { width: `100%`, height: `600px` }}
></iframe>
);
}

View File

@ -2,40 +2,35 @@ import { useEffect, useState } from 'react';
import LoadingSpinner from './LoadingSpinner';
import { VegaLite } from './VegaLite';
import loadData from '../lib/loadData';
import { Data } from '../types/properties';
type AxisType = 'quantitative' | 'temporal';
type TimeUnit = 'year' | 'yearmonth' | undefined; // or ...
type TimeUnit = 'year' | undefined; // or ...
export type LineChartProps = {
data: Omit<Data, 'csv'>;
data: Array<Array<string | number>> | string | { x: string; y: number }[];
title?: string;
xAxis: string;
xAxis?: string;
xAxisType?: AxisType;
xAxisTimeUnit?: TimeUnit;
yAxis: string | string[];
xAxisTimeUnit: TimeUnit;
yAxis?: string;
yAxisType?: AxisType;
fullWidth?: boolean;
symbol?: string;
};
export function LineChart({
data,
data = [],
fullWidth = false,
title = '',
xAxis,
xAxis = 'x',
xAxisType = 'temporal',
xAxisTimeUnit = 'year', // TODO: defaults to undefined would probably work better... keeping it as it's for compatibility purposes
yAxis,
yAxis = 'y',
yAxisType = 'quantitative',
symbol,
}: LineChartProps) {
const url = data.url;
const values = data.values;
const [isLoading, setIsLoading] = useState<boolean>(false);
// By default, assumes data is an Array...
const [specData, setSpecData] = useState<any>({ name: 'table' });
const isMultiYAxis = Array.isArray(yAxis);
const spec = {
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
@ -47,17 +42,8 @@ export function LineChart({
color: 'black',
strokeWidth: 1,
tooltip: true,
invalid: "break-paths"
},
data: specData,
...(isMultiYAxis
? {
transform: [
{ fold: yAxis, as: ['key', 'value'] },
{ filter: 'datum.value != null && datum.value != ""' }
],
}
: {}),
selection: {
grid: {
type: 'interval',
@ -71,35 +57,20 @@ export function LineChart({
type: xAxisType,
},
y: {
field: isMultiYAxis ? 'value' : yAxis,
field: yAxis,
type: yAxisType,
},
...(symbol
? {
color: {
field: symbol,
type: 'nominal',
},
}
: {}),
...(isMultiYAxis
? {
color: {
field: 'key',
type: 'nominal',
},
}
: {}),
},
} as any;
useEffect(() => {
if (url) {
// If data is string, assume it's a URL
if (typeof data === 'string') {
setIsLoading(true);
// Manualy loading the data allows us to do other kinds
// of stuff later e.g. load a file partially
loadData(url).then((res: any) => {
loadData(data).then((res: any) => {
setSpecData({ values: res, format: { type: 'csv' } });
setIsLoading(false);
});
@ -107,8 +78,12 @@ export function LineChart({
}, []);
var vegaData = {};
if (values) {
vegaData = { table: values };
if (Array.isArray(data)) {
var dataObj;
dataObj = data.map((r) => {
return { x: r[0], y: r[1] };
});
vegaData = { table: dataObj };
}
return isLoading ? (
@ -116,6 +91,6 @@ export function LineChart({
<LoadingSpinner />
</div>
) : (
<VegaLite data={vegaData} spec={spec} />
<VegaLite fullWidth={fullWidth} data={vegaData} spec={spec} />
);
}

View File

@ -1,8 +1,7 @@
import { CSSProperties, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import LoadingSpinner from './LoadingSpinner';
import loadData from '../lib/loadData';
import chroma from 'chroma-js';
import { GeospatialData } from '../types/properties';
import {
MapContainer,
TileLayer,
@ -12,34 +11,10 @@ import {
import 'leaflet/dist/leaflet.css';
import * as L from 'leaflet';
import providers from '../lib/tileLayerPresets';
type VariantKeys<T> = T extends { variants: infer V }
? {
[K in keyof V]: K extends string
? `${K}` | `${K}.${VariantKeys<V[K]>}`
: never;
}[keyof V]
: never;
type ProviderVariantKeys<T> = {
[K in keyof T]: K extends string
? `${K}` | `${K}.${VariantKeys<T[K]>}`
: never;
}[keyof T];
type TileLayerPreset = ProviderVariantKeys<typeof providers> | 'custom';
interface TileLayerSettings extends L.TileLayerOptions {
url?: string;
variant?: string | any;
}
export type MapProps = {
tileLayerName?: TileLayerPreset;
tileLayerOptions?: TileLayerSettings | undefined;
layers: {
data: GeospatialData;
data: string | GeoJSON.GeoJSON;
name: string;
colorScale?: {
starting: string;
@ -50,29 +25,14 @@ export type MapProps = {
propNames: string[];
}
| boolean;
_id?: number;
}[];
title?: string;
center?: { latitude: number | undefined; longitude: number | undefined };
zoom?: number;
style?: CSSProperties;
autoZoomConfiguration?: {
layerName: string;
};
};
const tileLayerDefaultName = process?.env
.NEXT_PUBLIC_MAP_TILE_LAYER_NAME as TileLayerPreset;
const tileLayerDefaultOptions = Object.keys(process?.env)
.filter((key) => key.startsWith('NEXT_PUBLIC_MAP_TILE_LAYER_OPTION_'))
.reduce((obj, key) => {
obj[key.split('NEXT_PUBLIC_MAP_TILE_LAYER_OPTION_')[1]] = process.env[key];
return obj;
}, {}) as TileLayerSettings;
export function Map({
tileLayerName = tileLayerDefaultName || 'OpenStreetMap',
tileLayerOptions,
layers = [
{
data: null,
@ -84,116 +44,23 @@ export function Map({
center = { latitude: 45, longitude: 45 },
zoom = 2,
title = '',
style = {},
autoZoomConfiguration = undefined,
}: MapProps) {
const [isLoading, setIsLoading] = useState<boolean>(false);
const [layersData, setLayersData] = useState<any>([]);
/*
tileLayerDefaultOptions
extract all environment variables thats starts with NEXT_PUBLIC_MAP_TILE_LAYER_OPTION_.
the variables names are the same as the TileLayer object properties:
- NEXT_PUBLIC_MAP_TILE_LAYER_OPTION_url:
- NEXT_PUBLIC_MAP_TILE_LAYER_OPTION_attribution
- NEXT_PUBLIC_MAP_TILE_LAYER_OPTION_accessToken
- NEXT_PUBLIC_MAP_TILE_LAYER_OPTION_id
- NEXT_PUBLIC_MAP_TILE_LAYER_OPTION_ext
- NEXT_PUBLIC_MAP_TILE_LAYER_OPTION_bounds
- NEXT_PUBLIC_MAP_TILE_LAYER_OPTION_maxZoom
- NEXT_PUBLIC_MAP_TILE_LAYER_OPTION_minZoom
see TileLayerOptions inteface
*/
//tileLayerData prioritizes properties passed through component over those passed through .env variables
tileLayerOptions = Object.assign(tileLayerDefaultOptions, tileLayerOptions);
let provider = {
url: tileLayerOptions.url,
options: tileLayerOptions,
};
if (tileLayerName != 'custom') {
var parts = tileLayerName.split('.');
var providerName = parts[0];
var variantName: string = parts[1];
//make sure to declare a variant if url depends on a variant: assume first
if (providers[providerName].url?.includes('{variant}') && !variantName)
variantName = Object.keys(providers[providerName].variants)[0];
if (!providers[providerName]) {
throw 'No such provider (' + providerName + ')';
}
provider = {
url: providers[providerName].url,
options: providers[providerName].options,
};
// overwrite values in provider from variant.
if (variantName && 'variants' in providers[providerName]) {
if (!(variantName in providers[providerName].variants)) {
throw 'No such variant of ' + providerName + ' (' + variantName + ')';
}
var variant = providers[providerName].variants[variantName];
var variantOptions;
if (typeof variant === 'string') {
variantOptions = {
variant: variant,
};
} else {
variantOptions = variant.options;
}
provider = {
url: variant.url || provider.url,
options: L.Util.extend({}, provider.options, variantOptions),
};
}
var attributionReplacer = function (attr) {
if (attr.indexOf('{attribution.') === -1) {
return attr;
}
return attr.replace(
/\{attribution.(\w*)\}/g,
function (match: any, attributionName: string) {
match;
return attributionReplacer(
providers[attributionName].options.attribution
);
}
);
};
provider.options.attribution = attributionReplacer(
provider.options.attribution
);
}
var tileLayerData = L.Util.extend(
{
url: provider.url,
},
provider.options,
tileLayerOptions
);
useEffect(() => {
const loadDataPromises = layers.map(async (layer) => {
const url = layer.data.url;
const geojson = layer.data.geojson;
let layerData: any;
if (url) {
if (typeof layer.data === 'string') {
// If "data" is string, assume it's a URL
setIsLoading(true);
layerData = await loadData(url).then((res: any) => {
layerData = await loadData(layer.data).then((res: any) => {
return JSON.parse(res);
});
} else {
// Else, expect raw GeoJSON
layerData = geojson;
layerData = layer.data;
}
if (layer.colorScale) {
@ -225,12 +92,10 @@ export function Map({
</div>
) : (
<MapContainer
key={layersData}
center={[center.latitude, center.longitude]}
zoom={zoom}
scrollWheelZoom={false}
className="h-80 w-full"
style={style ?? {}}
// @ts-ignore
whenReady={(map: any) => {
// Enable zoom using scroll wheel
@ -250,28 +115,12 @@ export function Map({
};
if (title) info.addTo(map.target);
if (!autoZoomConfiguration) return;
let layerToZoomBounds = L.latLngBounds(L.latLng(0, 0), L.latLng(0, 0));
layers.forEach((layer) => {
if (layer.name === autoZoomConfiguration.layerName) {
const data = layersData.find(
(layerData) => layerData.name === layer.name
)?.data;
if (data) {
layerToZoomBounds = L.geoJSON(data).getBounds();
return;
}
}
});
map.target.fitBounds(layerToZoomBounds);
}}
>
{tileLayerData.url && <TileLayer {...tileLayerData} />}
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<LayersControl position="bottomright">
{layers.map((layer) => {
const data = layersData.find(

View File

@ -1,24 +1,22 @@
// Core viewer
import { Viewer, Worker, SpecialZoomLevel } from '@react-pdf-viewer/core';
import { defaultLayoutPlugin } from '@react-pdf-viewer/default-layout';
import { Data } from '../types/properties';
// Import styles
import '@react-pdf-viewer/core/lib/styles/index.css';
import '@react-pdf-viewer/default-layout/lib/styles/index.css';
export interface PdfViewerProps {
data: Required<Pick<Data, 'url'>>;
url: string;
layout: boolean;
parentClassName?: string;
}
export function PdfViewer({
data,
url,
layout = false,
parentClassName = 'h-screen',
parentClassName,
}: PdfViewerProps) {
const url = data.url;
const defaultLayoutPluginInstance = defaultLayoutPlugin();
return (
<Worker workerUrl="https://unpkg.com/pdfjs-dist@2.15.349/build/pdf.worker.js">

View File

@ -1,9 +0,0 @@
import Plot, { PlotParams } from "react-plotly.js";
export const Plotly: React.FC<PlotParams> = (props) => {
return (
<div>
<Plot {...props} />
</div>
);
};

View File

@ -1,153 +0,0 @@
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
import { Plotly } from './Plotly';
import Papa, { ParseConfig } from 'papaparse';
import LoadingSpinner from './LoadingSpinner';
import { Data } from '../types/properties';
const queryClient = new QueryClient();
async function getCsv(url: string, bytes: number) {
const response = await fetch(url, {
headers: {
Range: `bytes=0-${bytes}`,
},
});
const data = await response.text();
return data;
}
async function parseCsv(
file: string,
parsingConfig: ParseConfig
): Promise<any> {
return new Promise((resolve, reject) => {
Papa.parse(file, {
...parsingConfig,
header: true,
dynamicTyping: true,
skipEmptyLines: true,
transform: (value: string): string => {
return value.trim();
},
complete: (results: any) => {
return resolve(results);
},
error: (error: any) => {
return reject(error);
},
});
});
}
export interface PlotlyBarChartProps {
data: Data;
uniqueId?: number;
bytes?: number;
parsingConfig?: ParseConfig;
xAxis: string;
yAxis: string;
// TODO: commented out because this doesn't work. I believe
// this would only make any difference on charts with multiple
// traces.
// lineLabel?: string;
title?: string;
}
export const PlotlyBarChart: React.FC<PlotlyBarChartProps> = ({
data,
bytes = 5132288,
parsingConfig = {},
xAxis,
yAxis,
// lineLabel,
title = '',
}) => {
const uniqueId = Math.random();
return (
// Provide the client to your App
<QueryClientProvider client={queryClient}>
<PlotlyBarChartInner
data={data}
uniqueId={uniqueId}
bytes={bytes}
parsingConfig={parsingConfig}
xAxis={xAxis}
yAxis={yAxis}
// lineLabel={lineLabel ?? yAxis}
title={title}
/>
</QueryClientProvider>
);
};
const PlotlyBarChartInner: React.FC<PlotlyBarChartProps> = ({
data,
uniqueId,
bytes,
parsingConfig,
xAxis,
yAxis,
// lineLabel,
title,
}) => {
if (data.values) {
return (
<div className="w-full" style={{ height: '500px' }}>
<Plotly
layout={{
title,
}}
data={[
{
x: data.values.map((d) => d[xAxis]),
y: data.values.map((d) => d[yAxis]),
type: 'bar',
// name: lineLabel,
},
]}
/>
</div>
);
}
const { data: csvString, isLoading: isDownloadingCSV } = useQuery(
['dataCsv', data.url, uniqueId],
() => getCsv(data.url as string, bytes ?? 5132288),
{ enabled: !!data.url }
);
const { data: parsedData, isLoading: isParsing } = useQuery(
['dataPreview', csvString, uniqueId],
() =>
parseCsv(
data.csv ? (data.csv as string) : (csvString as string),
parsingConfig ?? {}
),
{ enabled: data.csv ? true : !!csvString }
);
if (isParsing || isDownloadingCSV)
<div className="w-full flex justify-center items-center h-[500px]">
<LoadingSpinner />
</div>;
if (parsedData)
return (
<div className="w-full" style={{ height: '500px' }}>
<Plotly
layout={{
title,
}}
data={[
{
x: parsedData.data.map((d: any) => d[xAxis]),
y: parsedData.data.map((d: any) => d[yAxis]),
type: 'bar',
// name: lineLabel, TODO: commented out because this doesn't work
},
]}
/>
</div>
);
return (
<div className="w-full flex justify-center items-center h-[500px]">
<LoadingSpinner />
</div>
);
};

View File

@ -1,155 +0,0 @@
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
import { Plotly } from './Plotly';
import Papa, { ParseConfig } from 'papaparse';
import LoadingSpinner from './LoadingSpinner';
import { Data } from '../types/properties';
const queryClient = new QueryClient();
async function getCsv(url: string, bytes: number) {
const response = await fetch(url, {
headers: {
Range: `bytes=0-${bytes}`,
},
});
const data = await response.text();
return data;
}
async function parseCsv(
file: string,
parsingConfig: ParseConfig
): Promise<any> {
return new Promise((resolve, reject) => {
Papa.parse(file, {
...parsingConfig,
header: true,
dynamicTyping: true,
skipEmptyLines: true,
transform: (value: string): string => {
return value.trim();
},
complete: (results: any) => {
return resolve(results);
},
error: (error: any) => {
return reject(error);
},
});
});
}
export interface PlotlyLineChartProps {
data: Data;
bytes?: number;
parsingConfig?: ParseConfig;
xAxis: string;
yAxis: string;
lineLabel?: string;
title?: string;
uniqueId?: number;
}
export const PlotlyLineChart: React.FC<PlotlyLineChartProps> = ({
data,
bytes = 5132288,
parsingConfig = {},
xAxis,
yAxis,
lineLabel,
title = '',
uniqueId,
}) => {
uniqueId = uniqueId ?? Math.random();
return (
// Provide the client to your App
<QueryClientProvider client={queryClient}>
<LineChartInner
data={data}
uniqueId={uniqueId}
bytes={bytes}
parsingConfig={parsingConfig}
xAxis={xAxis}
yAxis={yAxis}
lineLabel={lineLabel ?? yAxis}
title={title}
/>
</QueryClientProvider>
);
};
const LineChartInner: React.FC<PlotlyLineChartProps> = ({
data,
uniqueId,
bytes,
parsingConfig,
xAxis,
yAxis,
lineLabel,
title,
}) => {
const values = data.values;
const url = data.url;
const csv = data.csv;
if (values) {
return (
<div className="w-full" style={{ height: '500px' }}>
<Plotly
layout={{
title,
}}
data={[
{
x: values.map((d) => d[xAxis]),
y: values.map((d) => d[yAxis]),
mode: 'lines',
name: lineLabel,
},
]}
/>
</div>
);
}
const { data: csvString, isLoading: isDownloadingCSV } = useQuery(
['dataCsv', url, uniqueId],
() => getCsv(url as string, bytes ?? 5132288),
{ enabled: !!url }
);
const { data: parsedData, isLoading: isParsing } = useQuery(
['dataPreview', csvString, uniqueId],
() =>
parseCsv(
csv ? (csv as string) : (csvString as string),
parsingConfig ?? {}
),
{ enabled: csv ? true : !!csvString }
);
if (isParsing || isDownloadingCSV)
<div className="w-full flex justify-center items-center h-[500px]">
<LoadingSpinner />
</div>;
if (parsedData)
return (
<div className="w-full" style={{ height: '500px' }}>
<Plotly
layout={{
title,
}}
data={[
{
x: parsedData.data.map((d: any) => d[xAxis]),
y: parsedData.data.map((d: any) => d[yAxis]),
mode: 'lines',
name: lineLabel,
},
]}
/>
</div>
);
return (
<div className="w-full flex justify-center items-center h-[500px]">
<LoadingSpinner />
</div>
);
};

View File

@ -6,8 +6,6 @@ import {
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
PaginationState,
Table as ReactTable,
useReactTable,
} from '@tanstack/react-table';
@ -27,19 +25,12 @@ import DebouncedInput from './DebouncedInput';
import loadData from '../lib/loadData';
import LoadingSpinner from './LoadingSpinner';
export type TableData = { cols: {key: string, name: string}[]; data: any[]; total: number };
export type TableProps = {
data?: Array<{ [key: string]: number | string }>;
cols?: Array<{ [key: string]: string }>;
csv?: string;
url?: string;
fullWidth?: boolean;
datastoreConfig?: {
dataStoreURI: string;
rowsPerPage?: number;
dataMapperFn: (data) => Promise<TableData> | TableData;
};
};
export const Table = ({
@ -48,28 +39,8 @@ export const Table = ({
csv = '',
url = '',
fullWidth = false,
datastoreConfig,
}: TableProps) => {
const [isLoading, setIsLoading] = useState<boolean>(false);
const [pageMap, setPageMap] = useState(new Map<number, boolean>());
const {
dataMapperFn,
dataStoreURI,
rowsPerPage = 10,
} = datastoreConfig ?? {};
const [globalFilter, setGlobalFilter] = useState('');
const [isLoadingPage, setIsLoadingPage] = useState<boolean>(false);
const [totalOfRows, setTotalOfRows] = useState<number>(0);
const [{ pageIndex, pageSize }, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: rowsPerPage,
});
const [lastIndex, setLastIndex] = useState(pageSize);
const [startIndex, setStartIndex] = useState(0);
const [hasSorted, setHasSorted] = useState(false);
if (csv) {
const out = parseCsv(csv);
@ -91,56 +62,21 @@ export const Table = ({
);
}, [data, cols]);
let table: ReactTable<unknown>;
const [globalFilter, setGlobalFilter] = useState('');
if (datastoreConfig) {
useEffect(() => {
setIsLoading(true);
fetch(`${dataStoreURI}&limit=${rowsPerPage}&offset=0`)
.then((res) => res.json())
.then(async (res) => {
const { data, cols, total } = await dataMapperFn(res);
setData(data);
setCols(cols);
setTotalOfRows(Math.ceil(total / rowsPerPage));
pageMap.set(0, true);
})
.finally(() => setIsLoading(false));
}, [dataStoreURI]);
table = useReactTable({
data,
pageCount: totalOfRows,
columns: tableCols,
getCoreRowModel: getCoreRowModel(),
state: {
pagination: { pageIndex, pageSize },
},
getFilteredRowModel: getFilteredRowModel(),
manualPagination: true,
onPaginationChange: setPagination,
getSortedRowModel: getSortedRowModel(),
});
useEffect(() => {
if (!hasSorted) return;
queryDataByText(globalFilter);
}, [table.getState().sorting]);
} else {
table = useReactTable({
data,
columns: tableCols,
getCoreRowModel: getCoreRowModel(),
state: {
globalFilter,
},
globalFilterFn: globalFilterFn,
onGlobalFilterChange: setGlobalFilter,
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
});
}
const table = useReactTable({
data,
columns: tableCols,
getCoreRowModel: getCoreRowModel(),
state: {
globalFilter,
},
globalFilterFn: globalFilterFn,
onGlobalFilterChange: setGlobalFilter,
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
});
useEffect(() => {
if (url) {
@ -155,70 +91,6 @@ export const Table = ({
}
}, [url]);
const queryDataByText = (filter) => {
setIsLoadingPage(true);
const sortedParam = getSortParam();
fetch(
`${dataStoreURI}&limit=${rowsPerPage}&offset=0&q=${filter}${sortedParam}`
)
.then((res) => res.json())
.then(async (res) => {
const { data, total = 0 } = await dataMapperFn(res);
setTotalOfRows(Math.ceil(total / rowsPerPage));
setData(data);
const newMap = new Map();
newMap.set(0, true);
setPageMap(newMap);
table.setPageIndex(0);
setStartIndex(0);
setLastIndex(pageSize);
})
.finally(() => setIsLoadingPage(false));
};
const getSortParam = () => {
const sort = table.getState().sorting;
return sort.length == 0
? ``
: '&sort=' +
sort
.map(
(x, i) =>
`${x.id}${
i === sort.length - 1 ? (x.desc ? ` desc` : ` asc`) : `,`
}`
)
.reduce((x1, x2) => x1 + x2);
};
const queryPaginatedData = (newPageIndex) => {
let newStartIndex = newPageIndex * pageSize;
setStartIndex(newStartIndex);
setLastIndex(newStartIndex + pageSize);
if (!pageMap.get(newPageIndex)) pageMap.set(newPageIndex, true);
else return;
const sortedParam = getSortParam();
setIsLoadingPage(true);
fetch(
`${dataStoreURI}&limit=${rowsPerPage}&offset=${
newStartIndex + pageSize
}&q=${globalFilter}${sortedParam}`
)
.then((res) => res.json())
.then(async (res) => {
const { data: responseData } = await dataMapperFn(res);
responseData.forEach((e) => {
data[newStartIndex] = e;
newStartIndex++;
});
setData([...data]);
})
.finally(() => setIsLoadingPage(false));
};
return isLoading ? (
<div className="w-full h-full min-h-[500px] flex items-center justify-center">
<LoadingSpinner />
@ -227,10 +99,7 @@ export const Table = ({
<div className={`${fullWidth ? 'w-[90vw] ml-[calc(50%-45vw)]' : 'w-full'}`}>
<DebouncedInput
value={globalFilter ?? ''}
onChange={(value: any) => {
if (datastoreConfig) queryDataByText(String(value));
setGlobalFilter(String(value));
}}
onChange={(value: any) => setGlobalFilter(String(value))}
className="p-2 text-sm shadow border border-block"
placeholder="Search all columns..."
/>
@ -245,10 +114,7 @@ export const Table = ({
className: h.column.getCanSort()
? 'cursor-pointer select-none'
: '',
onClick: (v) => {
setHasSorted(true);
h.column.getToggleSortingHandler()(v);
},
onClick: h.column.getToggleSortingHandler(),
}}
>
{flexRender(h.column.columnDef.header, h.getContext())}
@ -269,28 +135,15 @@ export const Table = ({
))}
</thead>
<tbody>
{datastoreConfig && isLoadingPage ? (
<tr>
<td colSpan={cols.length} rowSpan={cols.length}>
<div className="w-full h-full flex items-center justify-center pt-6">
<LoadingSpinner />
</div>
</td>
{table.getRowModel().rows.map((r) => (
<tr key={r.id} className="border-b border-b-slate-200">
{r.getVisibleCells().map((c) => (
<td key={c.id} className="py-2">
{flexRender(c.column.columnDef.cell, c.getContext())}
</td>
))}
</tr>
) : (
(datastoreConfig
? table.getRowModel().rows.slice(startIndex, lastIndex)
: table.getRowModel().rows
).map((r) => (
<tr key={r.id} className="border-b border-b-slate-200">
{r.getVisibleCells().map((c) => (
<td key={c.id} className="py-2">
{flexRender(c.column.columnDef.cell, c.getContext())}
</td>
))}
</tr>
))
)}
))}
</tbody>
</table>
<div className="flex gap-2 items-center justify-center mt-10">
@ -298,10 +151,7 @@ export const Table = ({
className={`w-6 h-6 ${
!table.getCanPreviousPage() ? 'opacity-25' : 'opacity-100'
}`}
onClick={() => {
if (datastoreConfig) queryPaginatedData(0);
table.setPageIndex(0);
}}
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
<ChevronDoubleLeftIcon />
@ -310,12 +160,7 @@ export const Table = ({
className={`w-6 h-6 ${
!table.getCanPreviousPage() ? 'opacity-25' : 'opacity-100'
}`}
onClick={() => {
if (datastoreConfig) {
queryPaginatedData(table.getState().pagination.pageIndex - 1);
}
table.previousPage();
}}
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
<ChevronLeftIcon />
@ -331,11 +176,7 @@ export const Table = ({
className={`w-6 h-6 ${
!table.getCanNextPage() ? 'opacity-25' : 'opacity-100'
}`}
onClick={() => {
if (datastoreConfig)
queryPaginatedData(table.getState().pagination.pageIndex + 1);
table.nextPage();
}}
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
<ChevronRightIcon />
@ -344,11 +185,7 @@ export const Table = ({
className={`w-6 h-6 ${
!table.getCanNextPage() ? 'opacity-25' : 'opacity-100'
}`}
onClick={() => {
const pageIndexToNavigate = table.getPageCount() - 1;
if (datastoreConfig) queryPaginatedData(pageIndexToNavigate);
table.setPageIndex(pageIndexToNavigate);
}}
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
<ChevronDoubleRightIcon />

View File

@ -1,7 +1,6 @@
// Wrapper for the Vega component
import { Vega as VegaOg } from "react-vega";
import { VegaProps } from "react-vega/lib/Vega";
export function Vega(props: VegaProps) {
export function Vega(props) {
return <VegaOg {...props} />;
}

View File

@ -1,9 +1,8 @@
// Wrapper for the Vega Lite component
import { VegaLite as VegaLiteOg } from 'react-vega';
import { VegaLiteProps } from 'react-vega/lib/VegaLite';
import applyFullWidthDirective from '../lib/applyFullWidthDirective';
import { VegaLite as VegaLiteOg } from "react-vega";
import applyFullWidthDirective from "../lib/applyFullWidthDirective";
export function VegaLite(props: VegaLiteProps) {
export function VegaLite(props) {
const Component = applyFullWidthDirective({ Component: VegaLiteOg });
return <Component {...props} />;

View File

@ -1,17 +1,10 @@
export * from './components/Table';
export * from './components/Catalog';
export * from './components/LineChart';
export * from './components/Vega';
export * from './components/VegaLite';
export * from './components/FlatUiTable';
export * from './components/OpenLayers/OpenLayers';
export * from './components/Map';
export * from './components/PdfViewer';
export * from "./components/Excel";
export * from "./components/Iframe";
export * from "./components/Plotly";
export * from "./components/PlotlyLineChart";
export * from "./components/PlotlyBarChart";
// NOTE: components that are hidden for now
// TODO: deprecate those components?
// export * from './components/Table';
// export * from "./components/BucketViewer";
// export * from './components/OpenLayers/OpenLayers';

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
/*
* All components should use this interface for
* its data property.
* Based on vega.
*
*/
type URL = string; // Just in case we want to transform it into an object with configurations
export interface Data {
url?: URL;
values?: { [key: string]: number | string | null | undefined }[];
csv?: string;
}
export interface GeospatialData {
url?: URL;
geojson?: GeoJSON.GeoJSON;
}

View File

@ -1,100 +0,0 @@
// NOTE: this component was renamed with .bkp so that it's hidden
// from the Storybook app
import { type Meta, type StoryObj } from '@storybook/react';
import {
BucketViewer,
BucketViewerProps,
} from '../src/components/BucketViewer';
import LoadingSpinner from '../src/components/LoadingSpinner';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta = {
title: 'Components/BucketViewer',
component: BucketViewer,
tags: ['autodocs'],
argTypes: {
domain: {
description: 'Bucket domain URI',
},
suffix: {
description: 'Suffix of bucket domain',
},
downloadConfig: {
description: `Bucket file download configuration`,
},
filterState: {
description: `State with values used to filter the bucket files`,
},
paginationConfig: {
description: `Configuration to show and stylise the pagination on the component`,
},
},
};
export default meta;
type Story = StoryObj<BucketViewerProps>;
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const Normal: Story = {
name: 'Bucket viewer',
args: {
domain: 'https://ssen-smart-meter.datopian.workers.dev',
suffix: '/',
dataMapperFn: async (rawData: Response) => {
const result = await rawData.json();
return result.objects.map((e) => ({
downloadFileUri: e.downloadLink,
fileName: e.key.replace(/^(\w+\/)/g, ''),
dateProps: {
date: new Date(e.uploaded),
dateFormatter: (date) => date.toLocaleDateString(),
},
}));
},
},
};
export const WithPagination: Story = {
name: 'With pagination',
args: {
domain: 'https://ssen-smart-meter.datopian.workers.dev',
suffix: '/',
paginationConfig: {
itemsPerPage: 3,
},
dataMapperFn: async (rawData: Response) => {
const result = await rawData.json();
return result.objects.map((e) => ({
downloadFileUri: e.downloadLink,
fileName: e.key.replace(/^(\w+\/)/g, ''),
dateProps: {
date: new Date(e.uploaded),
dateFormatter: (date) => date.toLocaleDateString(),
},
}));
},
},
};
export const WithComponentOnHoverOfEachBucketFile: Story = {
name: 'With component on hover of each bucket file',
args: {
domain: 'https://ssen-smart-meter.datopian.workers.dev',
suffix: '/',
downloadConfig: { hoverOfTheFileComponent: `HOVER COMPONENT` },
dataMapperFn: async (rawData: Response) => {
const result = await rawData.json();
return result.objects.map((e) => ({
downloadFileUri: e.downloadLink,
fileName: e.key.replace(/^(\w+\/)/g, ''),
dateProps: {
date: new Date(e.uploaded),
dateFormatter: (date) => date.toLocaleDateString(),
},
}));
},
},
};

View File

@ -10,14 +10,11 @@ const meta: Meta = {
argTypes: {
datasets: {
description:
"Array of items to be displayed on the searchable list. Must have the following properties: \n\n \
`_id`: item's unique id \n\n \
`url_path`: href of the item \n\n \
`metadata`: object with a `title` property, that will be displayed as the title of the item, together with any other custom fields that might or not be faceted.",
'Lists of datasets to be displayed in the list, will usually be automatically available',
},
facets: {
description:
"Array of strings, which are name of properties in the datasets' `metadata`, which are going to be faceted.",
'List of frontmatter fields that should be used as filters, needs to match exactly with the field name',
},
},
};
@ -34,42 +31,7 @@ export const WithoutFacets: Story = {
{
_id: '07026b22d49916754df1dc8ffb9ccd1c31878aae',
url_path: 'dataset-4',
metadata: {
title: 'Detecting Abusive Albanian',
},
},
{
_id: '42c86cf3c4fbbab11d91c2a7d6dcb8f750bc4e19',
url_path: 'dataset-1',
metadata: {
title: 'AbuseEval v1.0',
},
},
{
_id: '80001dd32a752421fdcc64e91fbd237dc31d6bb3',
url_path: 'dataset-2',
metadata: {
title:
'Abusive Language Detection on Arabic Social Media (Al Jazeera)',
},
},
{
_id: '96649d05d8193f4333b10015af76c6562971bd8c',
url_path: 'dataset-3',
metadata: {
title: 'CoRAL: a Context-aware Croatian Abusive Language Dataset',
},
},
],
},
};
export const WithFacets: Story = {
name: 'Catalog with facets',
args: {
datasets: [
{
_id: '07026b22d49916754df1dc8ffb9ccd1c31878aae',
url_path: 'dataset-4',
file_path: 'content/dataset-4/index.md',
metadata: {
title: 'Detecting Abusive Albanian',
'link-to-publication': 'https://arxiv.org/abs/2107.13592',
@ -158,6 +120,107 @@ export const WithFacets: Story = {
},
},
],
facets: ['language', 'platform'],
},
};
;
export const WithFacets: Story = {
name: 'Catalog with facets',
args: {
datasets: [
{
_id: '07026b22d49916754df1dc8ffb9ccd1c31878aae',
url_path: 'dataset-4',
file_path: 'content/dataset-4/index.md',
metadata: {
title: 'Detecting Abusive Albanian',
'link-to-publication': 'https://arxiv.org/abs/2107.13592',
'link-to-data': 'https://doi.org/10.6084/m9.figshare.19333298.v1',
'task-description':
'Hierarchical (offensive/not; untargeted/targeted; person/group/other)',
'details-of-task':
'Detect and categorise abusive language in social media data',
'size-of-dataset': 11874,
'percentage-abusive': 13.2,
language: 'Albanian',
'level-of-annotation': ['Posts'],
platform: ['Instagram', 'Youtube'],
medium: ['Text'],
reference:
'Nurce, E., Keci, J., Derczynski, L., 2021. Detecting Abusive Albanian. arXiv:2107.13592',
},
},
{
_id: '42c86cf3c4fbbab11d91c2a7d6dcb8f750bc4e19',
url_path: 'dataset-1',
file_path: 'content/dataset-1/index.md',
metadata: {
title: 'AbuseEval v1.0',
'link-to-publication':
'http://www.lrec-conf.org/proceedings/lrec2020/pdf/2020.lrec-1.760.pdf',
'link-to-data': 'https://github.com/tommasoc80/AbuseEval',
'task-description':
'Explicitness annotation of offensive and abusive content',
'details-of-task':
'Enriched versions of the OffensEval/OLID dataset with the distinction of explicit/implicit offensive messages and the new dimension for abusive messages. Labels for offensive language: EXPLICIT, IMPLICT, NOT; Labels for abusive language: EXPLICIT, IMPLICT, NOTABU',
'size-of-dataset': 14100,
'percentage-abusive': 20.75,
language: 'English',
'level-of-annotation': ['Tweets'],
platform: ['Twitter'],
medium: ['Text'],
reference:
'Caselli, T., Basile, V., Jelena, M., Inga, K., and Michael, G. 2020. "I feel offended, dont be abusive! implicit/explicit messages in offensive and abusive language". The 12th Language Resources and Evaluation Conference (pp. 6193-6202). European Language Resources Association.',
},
},
{
_id: '80001dd32a752421fdcc64e91fbd237dc31d6bb3',
url_path: 'dataset-2',
file_path: 'content/dataset-2/index.md',
metadata: {
title:
'Abusive Language Detection on Arabic Social Media (Al Jazeera)',
'link-to-publication': 'https://www.aclweb.org/anthology/W17-3008',
'link-to-data':
'http://alt.qcri.org/~hmubarak/offensive/AJCommentsClassification-CF.xlsx',
'task-description':
'Ternary (Obscene, Offensive but not obscene, Clean)',
'details-of-task': 'Incivility',
'size-of-dataset': 32000,
'percentage-abusive': 0.81,
language: 'Arabic',
'level-of-annotation': ['Posts'],
platform: ['AlJazeera'],
medium: ['Text'],
reference:
'Mubarak, H., Darwish, K. and Magdy, W., 2017. Abusive Language Detection on Arabic Social Media. In: Proceedings of the First Workshop on Abusive Language Online. Vancouver, Canada: Association for Computational Linguistics, pp.52-56.',
},
},
{
_id: '96649d05d8193f4333b10015af76c6562971bd8c',
url_path: 'dataset-3',
file_path: 'content/dataset-3/index.md',
metadata: {
title: 'CoRAL: a Context-aware Croatian Abusive Language Dataset',
'link-to-publication':
'https://aclanthology.org/2022.findings-aacl.21/',
'link-to-data':
'https://github.com/shekharRavi/CoRAL-dataset-Findings-of-the-ACL-AACL-IJCNLP-2022',
'task-description':
'Multi-class based on context dependency categories (CDC)',
'details-of-task': 'Detectioning CDC from abusive comments',
'size-of-dataset': 2240,
'percentage-abusive': 100,
language: 'Croatian',
'level-of-annotation': ['Posts'],
platform: ['Posts'],
medium: ['Newspaper Comments'],
reference:
'Ravi Shekhar, Mladen Karan and Matthew Purver (2022). CoRAL: a Context-aware Croatian Abusive Language Dataset. Findings of the ACL: AACL-IJCNLP.',
},
},
],
facets: ['language', 'platform']
},
};
;

View File

@ -4,13 +4,13 @@ import { Excel, ExcelProps } from '../src/components/Excel';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta = {
title: 'Components/Tabular/Excel',
title: 'Components/Excel',
component: Excel,
tags: ['autodocs'],
argTypes: {
data: {
url: {
description:
'Object with a `url` property pointing to the Excel file to be displayed, e.g.: `{ url: "https://url.to/data.csv" }`',
'Url of the file to be displayed e.g.: "https://url.to/data.csv"',
},
},
};
@ -22,17 +22,13 @@ type Story = StoryObj<ExcelProps>;
export const SingleSheet: Story = {
name: 'Excel file with just one sheet',
args: {
data: {
url: 'https://sheetjs.com/pres.xlsx',
},
url: 'https://sheetjs.com/pres.xlsx',
},
};
export const MultipleSheet: Story = {
name: 'Excel file with multiple sheets',
args: {
data: {
url: 'https://storage.portaljs.org/IC-Gantt-Chart-Project-Template-8857.xlsx',
},
url: 'https://storage.portaljs.org/IC-Gantt-Chart-Project-Template-8857.xlsx',
},
};

View File

@ -4,31 +4,29 @@ import { FlatUiTable, FlatUiTableProps } from '../src/components/FlatUiTable';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta = {
title: 'Components/Tabular/FlatUiTable',
title: 'Components/FlatUiTable',
component: FlatUiTable,
tags: ['autodocs'],
argTypes: {
data: {
description:
'Data to be displayed. \n\n \
Must be an object with one of the following properties: `url`, `values` or `csv` \n\n \
`url`: URL pointing to a CSV file. \n\n \
`values`: array of objects. \n\n \
`csv`: string with valid CSV. \n\n \
',
'Data to be displayed in the table, must be setup as an array of key value pairs',
},
csv: {
description: 'CSV data as string.',
},
url: {
description:
'Fetch the data from a CSV file remotely. only the first 5MB of data will be displayed',
},
bytes: {
description:
'Fetch the data from a CSV file remotely. Only the first <bytes> of data will be displayed. Defaults to 5MB.',
'Fetch the data from a CSV file remotely. only the first <bytes> of data will be displayed',
},
parsingConfig: {
description:
'Configuration for parsing the CSV data. See https://www.papaparse.com/docs#config for more details',
},
uniqueId: {
description:
'Provide a unique ID to help with cache revalidation of the fetched data.',
},
},
};
@ -38,40 +36,34 @@ type Story = StoryObj<FlatUiTableProps>;
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const FromColumnsAndData: Story = {
name: 'Table from array or objects',
name: 'Table data',
args: {
data: {
values: [
{ id: 1, lastName: 'Snow', firstName: 'Jon', age: 35 },
{ id: 2, lastName: 'Lannister', firstName: 'Cersei', age: 42 },
{ id: 3, lastName: 'Lannister', firstName: 'Jaime', age: 45 },
{ id: 4, lastName: 'Stark', firstName: 'Arya', age: 16 },
{ id: 7, lastName: 'Clifford', firstName: 'Ferrara', age: 44 },
{ id: 8, lastName: 'Frances', firstName: 'Rossini', age: 36 },
{ id: 9, lastName: 'Roxie', firstName: 'Harvey', age: 65 },
],
},
data: [
{ id: 1, lastName: 'Snow', firstName: 'Jon', age: 35 },
{ id: 2, lastName: 'Lannister', firstName: 'Cersei', age: 42 },
{ id: 3, lastName: 'Lannister', firstName: 'Jaime', age: 45 },
{ id: 4, lastName: 'Stark', firstName: 'Arya', age: 16 },
{ id: 7, lastName: 'Clifford', firstName: 'Ferrara', age: 44 },
{ id: 8, lastName: 'Frances', firstName: 'Rossini', age: 36 },
{ id: 9, lastName: 'Roxie', firstName: 'Harvey', age: 65 },
],
},
};
export const FromRawCSV: Story = {
name: 'Table from inline CSV',
name: 'Table from raw CSV',
args: {
data: {
csv: `
rawCsv: `
Year,Temp Anomaly
1850,-0.418
2020,0.923
`,
},
},
};
export const FromURL: Story = {
name: 'Table from URL',
args: {
data: {
url: 'https://storage.openspending.org/alberta-budget/__os_imported__alberta_total.csv',
},
url: 'https://ckan-dev.sse.datopian.com/datastore/dump/601c9cf0-595e-46d8-88fc-d1ab2904e2db',
},
};

View File

@ -1,33 +0,0 @@
import { type Meta, type StoryObj } from '@storybook/react';
import { Iframe, IframeProps } from '../src/components/Iframe';
const meta: Meta = {
title: 'Components/Embedding/Iframe',
component: Iframe,
tags: ['autodocs'],
argTypes: {
data: {
description:
'Object with a `url` property pointing to the page to be embeded.',
},
style: {
description:
'Style object of the component. See example at https://react.dev/learn#displaying-data. Defaults to `{ width: "100%", height: "100%" }`',
},
},
};
export default meta;
type Story = StoryObj<IframeProps>;
export const Normal: Story = {
name: 'Iframe',
args: {
data: {
url: 'https://app.powerbi.com/view?r=eyJrIjoiYzBmN2Q2MzYtYzE3MS00ODkxLWE5OWMtZTQ2MjBlMDljMDk4IiwidCI6Ijk1M2IwZjgzLTFjZTYtNDVjMy04MmM5LTFkODQ3ZTM3MjMzOSIsImMiOjh9',
},
style: { width: `100%`, height: `600px` },
},
};

View File

@ -4,6 +4,6 @@ import { Meta } from '@storybook/blocks';
# Welcome to the PortalJS components guide
**Official Website:** [portaljs.com](https://portaljs.com)
**Docs:** [portaljs.com/opensource](https://portaljs.com/opensource)
**Official Website:** [portaljs.org](https://portaljs.org)
**Docs:** [portaljs.org/docs](https://portaljs.org/docs)
**GitHub:** [github.com/datopian/portaljs](https://github.com/datopian/portaljs)

View File

@ -4,40 +4,37 @@ import { LineChart, LineChartProps } from '../src/components/LineChart';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta = {
title: 'Components/Charts/LineChart',
title: 'Components/LineChart',
component: LineChart,
tags: ['autodocs'],
argTypes: {
data: {
description:
'Data to be displayed. \n\n \
Must be an object with one of the following properties: `url` or `values` \n\n \
`url`: URL pointing to a CSV file. \n\n \
`values`: array of objects \n\n',
'Data to be displayed.\n\n E.g.: [["1990", 1], ["1991", 2]] \n\nOR\n\n "https://url.to/data.csv"',
},
title: {
description: 'Title to display on the chart.',
description: 'Title to display on the chart. Optional.',
},
xAxis: {
description:
'Name of the column header or object property that represents the X-axis on the data.',
'Name of the X axis on the data. Required when the "data" parameter is an URL.',
},
xAxisType: {
description: 'Type of the X-axis.',
description: 'Type of the X axis',
},
xAxisTimeUnit: {
description: 'Time unit of the X-axis, in case its type is `temporal.`',
description: 'Time unit of the X axis (optional)',
},
yAxis: {
description:
'Name of the column headers or object properties that represent the Y-axis on the data.',
'Name of the Y axis on the data. Required when the "data" parameter is an URL.',
},
yAxisType: {
description: 'Type of the Y-axis',
description: 'Type of the Y axis',
},
symbol: {
fullWidth: {
description:
'Name of the column header or object property that represents a series for multiple series.',
'Whether the component should be rendered as full bleed or not',
},
},
};
@ -50,112 +47,22 @@ type Story = StoryObj<LineChartProps>;
export const FromDataPoints: Story = {
name: 'Line chart from array of data points',
args: {
data: {
values: [
{ year: '1850', value: -0.41765878 },
{ year: '1851', value: -0.2333498 },
{ year: '1852', value: -0.22939907 },
{ year: '1853', value: -0.27035445 },
{ year: '1854', value: -0.29163003 },
],
},
xAxis: 'year',
yAxis: 'value',
},
};
export const MultiSeries: Story = {
name: 'Line chart with multiple series (specifying symbol)',
args: {
data: {
values: [
{ year: '1850', value: -0.41765878, z: 'A' },
{ year: '1851', value: -0.2333498, z: 'A' },
{ year: '1852', value: -0.22939907, z: 'A' },
{ year: '1853', value: -0.27035445, z: 'A' },
{ year: '1854', value: -0.29163003, z: 'A' },
{ year: '1850', value: -0.42993882, z: 'B' },
{ year: '1851', value: -0.30365549, z: 'B' },
{ year: '1852', value: -0.27905189, z: 'B' },
{ year: '1853', value: -0.22939704, z: 'B' },
{ year: '1854', value: -0.25688013, z: 'B' },
{ year: '1850', value: -0.4757164, z: 'C' },
{ year: '1851', value: -0.41971018, z: 'C' },
{ year: '1852', value: -0.40724799, z: 'C' },
{ year: '1853', value: -0.45049156, z: 'C' },
{ year: '1854', value: -0.41896583, z: 'C' },
],
},
xAxis: 'year',
yAxis: 'value',
symbol: 'z',
},
};
export const MultiColumns: Story = {
name: 'Line chart with multiple series (with multiple columns)',
args: {
data: {
values: [
{ year: '1850', A: -0.41765878, B: -0.42993882, C: -0.4757164 },
{ year: '1851', A: -0.2333498, B: -0.30365549, C: -0.41971018 },
{ year: '1852', A: -0.22939907, B: -0.27905189, C: -0.40724799 },
{ year: '1853', A: -0.27035445, B: -0.22939704, C: -0.45049156 },
{ year: '1854', A: -0.29163003, B: -0.25688013, C: -0.41896583 },
],
},
xAxis: 'year',
yAxis: ['A', 'B', 'C'],
data: [
['1850', -0.41765878],
['1851', -0.2333498],
['1852', -0.22939907],
['1853', -0.27035445],
['1854', -0.29163003],
],
},
};
export const FromURL: Story = {
name: 'Line chart from URL',
args: {
data: {
url: 'https://raw.githubusercontent.com/datasets/oil-prices/main/data/wti-year.csv',
},
title: 'Oil Price x Year',
data: 'https://raw.githubusercontent.com/datasets/oil-prices/main/data/wti-year.csv',
xAxis: 'Date',
yAxis: 'Price',
},
};
// export const FromURLMulti: Story = {
// name: 'Line chart from URL Multi Column',
// args: {
// data: {
// url: 'https://raw.githubusercontent.com/datasets/sea-level-rise/refs/heads/main/data/epa-sea-level.csv',
// },
// title: 'Sea Level Rise (1880-2023)',
// xAxis: 'Year',
// yAxis: ["CSIRO Adjusted Sea Level", "NOAA Adjusted Sea Level"],
// xAxisType: 'temporal',
// xAxisTimeUnit: 'year',
// yAxisType: 'quantitative'
// },
// };
// export const MultipleSeriesMissingValues: Story = {
// name: 'Line chart with missing values',
// args: {
// data: {
// values: [
// { year: '2020', seriesA: 10, seriesB: 15 },
// { year: '2021', seriesA: 20 }, // seriesB missing
// { year: '2022', seriesA: 15 }, // seriesB missing
// { year: '2023', seriesB: 30 }, // seriesA missing
// { year: '2024', seriesA: 25, seriesB: 35 },
// { year: '2024', seriesA: 20, seriesB: 40 },
// { year: '2024', seriesB: 45 },
// ],
// },
// title: 'Handling Missing Data Points',
// xAxis: 'year',
// yAxis: ['seriesA', 'seriesB'],
// xAxisType: 'temporal',
// xAxisTimeUnit: 'year',
// yAxisType: 'quantitative'
// },
// };

View File

@ -4,33 +4,22 @@ import { Map, MapProps } from '../src/components/Map';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta = {
title: 'Components/Geospatial/Map',
title: 'Components/Map',
component: Map,
tags: ['autodocs'],
argTypes: {
layers: {
description:
'Array of layers to be displayed on the map. Should be an object with: \n\n \
`data`: object with either a `url` property pointing to a GeoJSON file or a `geojson` property with a GeoJSON object. \n\n \
`name`: name of the layer. \n\n \
`colorscale`: object with a `starting` and `ending` colors that will be used to create a gradient and color the map. \n\n \
`tooltip`: `true` to show all available features on the tooltip, object with a `propNames` property as an array of strings to choose which features to display. \n\n',
'Data to be displayed.\n\n GeoJSON Object \n\nOR\n\n URL to GeoJSON Object',
},
title: {
description: 'Title to display on the map.',
description: 'Title to display on the map. Optional.',
},
center: {
description: 'Initial coordinates of the center of the map',
},
zoom: {
description: 'Initial zoom level',
},
style: {
description: "CSS styles to be applied to the map's container.",
},
autoZoomConfiguration: {
description:
"Pass a layer's name to automatically zoom to the bounding area of a layer.",
description: 'Zoom level',
},
},
};
@ -43,15 +32,9 @@ type Story = StoryObj<MapProps>;
export const GeoJSONPolygons: Story = {
name: 'GeoJSON polygons map',
args: {
tileLayerName:'MapBox',
tileLayerOptions:{
accessToken : 'pk.eyJ1Ijoid2lsbHktcGFsbWFyZWpvIiwiYSI6ImNqNzk5NmRpNDFzb2cyeG9sc2luMHNjajUifQ.lkoVRFSI8hOLH4uJeOzwXw',
},
layers: [
{
data: {
url: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_geography_marine_polys.geojson',
},
data: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_geography_marine_polys.geojson',
name: 'Polygons',
tooltip: { propNames: ['name'] },
colorScale: {
@ -71,9 +54,7 @@ export const GeoJSONPoints: Story = {
args: {
layers: [
{
data: {
url: 'https://opendata.arcgis.com/datasets/9c58741995174fbcb017cf46c8a42f4b_25.geojson',
},
data: 'https://opendata.arcgis.com/datasets/9c58741995174fbcb017cf46c8a42f4b_25.geojson',
name: 'Points',
tooltip: { propNames: ['Location'] },
},
@ -89,16 +70,12 @@ export const GeoJSONMultipleLayers: Story = {
args: {
layers: [
{
data: {
url: 'https://opendata.arcgis.com/datasets/9c58741995174fbcb017cf46c8a42f4b_25.geojson',
},
data: 'https://opendata.arcgis.com/datasets/9c58741995174fbcb017cf46c8a42f4b_25.geojson',
name: 'Points',
tooltip: true,
},
{
data: {
url: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_geography_marine_polys.geojson',
},
data: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_geography_marine_polys.geojson',
name: 'Polygons',
tooltip: true,
colorScale: {
@ -112,35 +89,3 @@ export const GeoJSONMultipleLayers: Story = {
zoom: 2,
},
};
export const GeoJSONMultipleLayersWithAutoZoomInSpecifiedLayer: Story = {
name: 'GeoJSON polygons and points map with auto zoom in the points layer',
args: {
layers: [
{
data: {
url: 'https://opendata.arcgis.com/datasets/9c58741995174fbcb017cf46c8a42f4b_25.geojson',
},
name: 'Points',
tooltip: true,
},
{
data: {
url: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_geography_marine_polys.geojson',
},
name: 'Polygons',
tooltip: true,
colorScale: {
starting: '#ff0000',
ending: '#00ff00',
},
},
],
title: 'Polygons and points',
center: { latitude: 45, longitude: 0 },
zoom: 2,
autoZoomConfiguration: {
layerName: 'Points',
},
},
};

View File

@ -1,6 +1,3 @@
// NOTE: this component was renamed with .bkp so that it's hidden
// from the Storybook app
import type { Meta, StoryObj } from '@storybook/react';
import React from 'react';
import OpenLayers from '../src/components/OpenLayers/OpenLayers';

View File

@ -3,21 +3,19 @@ import type { Meta, StoryObj } from '@storybook/react';
import { PdfViewer, PdfViewerProps } from '../src/components/PdfViewer';
const meta: Meta = {
title: 'Components/Embedding/PdfViewer',
title: 'Components/PdfViewer',
component: PdfViewer,
tags: ['autodocs'],
argTypes: {
data: {
description:
'Object with a `url` property pointing to the PDF file to be displayed, e.g.: `{ url: "https://cdn.filestackcontent.com/wcrjf9qPTCKXV3hMXDwK" }`.',
url: {
description: 'URL to PDF file',
},
parentClassName: {
description:
'HTML classes to be applied to the container of the PDF viewer. [Tailwind](https://tailwindcss.com/) classes, such as `h-96` to define the height of the component, can be used on this field.',
description: 'Classname for the parent div of the pdf viewer',
},
layout: {
layour: {
description:
'Set to `true` if you want to display a layout with zoom level, page count, printing button and other controls.',
'Set to true if you want to have a layout with zoom level, page count, printing button etc',
defaultValue: false,
},
},
@ -27,23 +25,26 @@ export default meta;
type Story = StoryObj<PdfViewerProps>;
export const PdfViewerStoryWithoutControlsLayout: Story = {
name: 'PDF Viewer without controls layout',
export const PdfViewerStory: Story = {
name: 'PdfViewer',
args: {
data: {
url: 'https://cdn.filestackcontent.com/wcrjf9qPTCKXV3hMXDwK',
},
parentClassName: 'h-96',
url: 'https://cdn.filestackcontent.com/wcrjf9qPTCKXV3hMXDwK',
},
};
export const PdfViewerStoryWithControlsLayout: Story = {
name: 'PdfViewer with controls layout',
export const PdfViewerStoryWithLayout: Story = {
name: 'PdfViewer with the default layout',
args: {
data: {
url: 'https://cdn.filestackcontent.com/wcrjf9qPTCKXV3hMXDwK',
},
url: 'https://cdn.filestackcontent.com/wcrjf9qPTCKXV3hMXDwK',
layout: true,
},
};
export const PdfViewerStoryWithHeight: Story = {
name: 'PdfViewer with a custom height',
args: {
url: 'https://cdn.filestackcontent.com/wcrjf9qPTCKXV3hMXDwK',
parentClassName: 'h-96',
layout: true,
parentClassName: 'h-96',
},
};

View File

@ -1,49 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Plotly } from '../src/components/Plotly';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta = {
title: 'Components/Charts/Plotly',
component: Plotly,
tags: ['autodocs'],
argTypes: {
data: {
description:
"Plotly's `data` prop. You can find references on how to use these props at https://github.com/plotly/react-plotly.js/#basic-props.",
},
layout: {
description:
"Plotly's `layout` prop. You can find references on how to use these props at https://github.com/plotly/react-plotly.js/#basic-props.",
},
},
};
export default meta;
type Story = StoryObj<any>;
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const Primary: Story = {
name: 'Line chart',
args: {
data: [
{
x: [1, 2, 3],
y: [2, 6, 3],
type: 'scatter',
mode: 'lines+markers',
marker: { color: 'red' },
},
],
layout: {
title: 'Chart built with Plotly',
xaxis: {
title: 'x Axis',
},
yaxis: {
title: 'y Axis',
},
},
},
};

View File

@ -1,102 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import {
PlotlyBarChart,
PlotlyBarChartProps,
} from '../src/components/PlotlyBarChart';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta = {
title: 'Components/Charts/PlotlyBarChart',
component: PlotlyBarChart,
tags: ['autodocs'],
argTypes: {
data: {
description:
'Data to be displayed. \n\n \
Must be an object with one of the following properties: `url`, `values` or `csv` \n\n \
`url`: URL pointing to a CSV file. \n\n \
`values`: array of objects (check out [this example](/?path=/story/components-plotlybarchart--from-data-points)) \n\n \
`csv`: string with valid CSV (check out [this example](/?path=/story/components-plotlybarchart--from-inline-csv)) \n\n \
',
},
bytes: {
// TODO: likely this should be an extra option on the data parameter,
// specific to URLs
description:
"How many bytes to read from the url so that the entire file doesn's have to be fetched.",
},
parsingConfig: {
description:
'If using URL or CSV, this parsing config will be used to parse the data. Check https://www.papaparse.com/ for more info.',
},
title: {
description: 'Title to display on the chart.',
},
// TODO: commented out because this doesn't work
// lineLabel: {
// description:
// 'Label to display on the line, Optional, will use yAxis if not provided',
// },
xAxis: {
description:
'Name of the column header or object property that represents the X-axis on the data.',
},
yAxis: {
description:
'Name of the column header or object property that represents the Y-axis on the data.',
},
uniqueId: {
description: 'Provide a unique ID to help with cache revalidation of the fetched data.'
}
},
};
export default meta;
type Story = StoryObj<PlotlyBarChartProps>;
export const FromDataPoints: Story = {
name: 'Bar chart from array of data points',
args: {
data: {
values: [
{ year: '1850', temperature: -0.41765878 },
{ year: '1851', temperature: -0.2333498 },
{ year: '1852', temperature: -0.22939907 },
{ year: '1853', temperature: -0.27035445 },
{ year: '1854', temperature: -0.29163003 },
],
},
xAxis: 'year',
yAxis: 'temperature',
},
};
export const FromURL: Story = {
name: 'Bar chart from URL',
args: {
title: 'Apple Stock Prices',
data: {
url: 'https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv',
},
xAxis: 'Date',
yAxis: 'AAPL.Open',
},
};
export const FromInlineCSV: Story = {
name: 'Bar chart from inline CSV',
args: {
title: 'Apple Stock Prices',
data: {
csv: `Date,AAPL.Open,AAPL.High,AAPL.Low,AAPL.Close,AAPL.Volume,AAPL.Adjusted,dn,mavg,up,direction
2015-02-17,127.489998,128.880005,126.919998,127.830002,63152400,122.905254,106.7410523,117.9276669,129.1142814,Increasing
2015-02-18,127.629997,128.779999,127.449997,128.720001,44891700,123.760965,107.842423,118.9403335,130.0382439,Increasing
2015-02-19,128.479996,129.029999,128.330002,128.449997,37362400,123.501363,108.8942449,119.8891668,130.8840887,Decreasing
2015-02-20,128.619995,129.5,128.050003,129.5,48948400,124.510914,109.7854494,120.7635001,131.7415509,Increasing`,
},
xAxis: 'Date',
yAxis: 'AAPL.Open',
},
};

View File

@ -1,101 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import {
PlotlyLineChart,
PlotlyLineChartProps,
} from '../src/components/PlotlyLineChart';
const meta: Meta = {
title: 'Components/Charts/PlotlyLineChart',
component: PlotlyLineChart,
tags: ['autodocs'],
argTypes: {
data: {
description:
'Data to be displayed. \n\n \
Must be an object with one of the following properties: `url`, `values` or `csv` \n\n \
`url`: URL pointing to a CSV file. \n\n \
`values`: array of objects. \n\n \
`csv`: string with valid CSV. \n\n \
',
},
bytes: {
// TODO: likely this should be an extra option on the data parameter,
// specific to URLs
description:
"How many bytes to read from the url so that the entire file doesn's have to be fetched.",
},
parsingConfig: {
description:
'If using URL or CSV, this parsing config will be used to parse the data. Check https://www.papaparse.com/ for more info',
},
title: {
description: 'Title to display on the chart.',
},
lineLabel: {
description:
'Label to display on the line, will use yAxis if not provided',
},
xAxis: {
description:
'Name of the column header or object property that represents the X-axis on the data.',
},
yAxis: {
description:
'Name of the column header or object property that represents the Y-axis on the data.',
},
uniqueId: {
description:
'Provide a unique ID to help with cache revalidation of the fetched data.',
},
},
};
export default meta;
type Story = StoryObj<PlotlyLineChartProps>;
export const FromDataPoints: Story = {
name: 'Line chart from array of data points',
args: {
data: {
values: [
{ year: '1850', temperature: -0.41765878 },
{ year: '1851', temperature: -0.2333498 },
{ year: '1852', temperature: -0.22939907 },
{ year: '1853', temperature: -0.27035445 },
{ year: '1854', temperature: -0.29163003 },
],
},
xAxis: 'year',
yAxis: 'temperature',
},
};
export const FromURL: Story = {
name: 'Line chart from URL',
args: {
title: 'Oil Price x Year',
data: {
url: 'https://raw.githubusercontent.com/datasets/oil-prices/main/data/wti-year.csv',
},
xAxis: 'Date',
yAxis: 'Price',
},
};
export const FromInlineCSV: Story = {
name: 'Bar chart from inline CSV',
args: {
title: 'Apple Stock Prices',
data: {
csv: `Date,AAPL.Open,AAPL.High,AAPL.Low,AAPL.Close,AAPL.Volume,AAPL.Adjusted,dn,mavg,up,direction
2015-02-17,127.489998,128.880005,126.919998,127.830002,63152400,122.905254,106.7410523,117.9276669,129.1142814,Increasing
2015-02-18,127.629997,128.779999,127.449997,128.720001,44891700,123.760965,107.842423,118.9403335,130.0382439,Increasing
2015-02-19,128.479996,129.029999,128.330002,128.449997,37362400,123.501363,108.8942449,119.8891668,130.8840887,Decreasing
2015-02-20,128.619995,129.5,128.050003,129.5,48948400,124.510914,109.7854494,120.7635001,131.7415509,Increasing`,
},
xAxis: 'Date',
yAxis: 'AAPL.Open',
},
};

View File

@ -1,33 +1,25 @@
// NOTE: this component was renamed with .bkp so that it's hidden
// from the Storybook app
import type { Meta, StoryObj } from '@storybook/react';
import { Table, TableProps } from '../src/components/Table';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta = {
title: 'Components/Tabular/Table',
title: 'Components/Table',
component: Table,
tags: ['autodocs'],
argTypes: {
data: {
description:
'Data to be displayed in the table, must also set "cols" to work.',
description: "Data to be displayed in the table, must also set \"cols\" to work."
},
cols: {
description:
'Columns to be displayed in the table, must also set "data" to work.',
description: "Columns to be displayed in the table, must also set \"data\" to work."
},
csv: {
description: 'CSV data as string.',
description: "CSV data as string.",
},
url: {
description: 'Fetch the data from a CSV file remotely.',
},
datastoreConfig: {
description: `Configuration to use CKAN's datastore API extension integrated with the component`,
},
description: "Fetch the data from a CSV file remotely."
}
},
};
@ -37,7 +29,7 @@ type Story = StoryObj<TableProps>;
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const FromColumnsAndData: Story = {
name: 'Table from columns and data',
name: "Table from columns and data",
args: {
data: [
{ id: 1, lastName: 'Snow', firstName: 'Jon', age: 35 },
@ -57,40 +49,21 @@ export const FromColumnsAndData: Story = {
},
};
export const WithDataStoreIntegration: Story = {
name: 'Table with datastore integration',
args: {
datastoreConfig: {
dataStoreURI: `https://www.civicdata.com/api/action/datastore_search?resource_id=46ec0807-31ff-497f-bfa0-f31c796cdee8`,
dataMapperFn: ({
result,
}: {
result: { fields: { id }[]; records: []; total: number };
}) => {
return {
data: result.records,
cols: result.fields.map((x) => ({ key: x.id, name: x.id })),
total: result.total,
};
},
},
},
};
export const FromRawCSV: Story = {
name: 'Table from raw CSV',
name: "Table from raw CSV",
args: {
csv: `
Year,Temp Anomaly
1850,-0.418
2020,0.923
`,
},
`
}
};
export const FromURL: Story = {
name: 'Table from URL',
name: "Table from URL",
args: {
url: 'https://raw.githubusercontent.com/datasets/finance-vix/main/data/vix-daily.csv',
},
url: "https://raw.githubusercontent.com/datasets/finance-vix/main/data/vix-daily.csv"
}
};

View File

@ -4,19 +4,9 @@ import { Vega } from '../src/components/Vega';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta = {
title: 'Components/Charts/Vega',
title: 'Components/Vega',
component: Vega,
tags: ['autodocs'],
argTypes: {
data: {
description:
"Vega's `data` prop. You can find references on how to use this prop at https://vega.github.io/vega/docs/data/",
},
spec: {
description:
"Vega's `spec` prop. You can find references on how to use this prop at https://vega.github.io/vega/docs/specification/",
},
},
};
export default meta;
@ -25,7 +15,7 @@ type Story = StoryObj<any>;
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const Primary: Story = {
name: 'Bar chart',
name: 'Chart built with Vega',
args: {
data: {
table: [

View File

@ -4,7 +4,7 @@ import { VegaLite } from '../src/components/VegaLite';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta = {
title: 'Components/Charts/VegaLite',
title: 'Components/VegaLite',
component: VegaLite,
tags: ['autodocs'],
argTypes: {
@ -25,7 +25,7 @@ type Story = StoryObj<any>;
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const Primary: Story = {
name: 'Bar chart',
name: 'Chart built with Vega Lite',
args: {
data: {
table: [

Some files were not shown because too many files have changed in this diff Show More