Compare commits

..

57 Commits

Author SHA1 Message Date
github-actions[bot]
1769b1ee0b Version Packages 2025-01-22 15:26:42 +00:00
Ola Rubaj
62dbc35d3b fix(LineChart): skip lines at invalid/missing data points (don't force connect) 2025-01-22 16:23:17 +01:00
Lucas Morais Bispo
12f0d0d732 Merge pull request #1347 from datopian/feature/update-links
[md][datahub] updated links from portaljs.org to portaljs.com
2025-01-13 08:52:50 -03:00
muhammad-hassan11
d80d1f5012 removed logs 2025-01-13 16:22:01 +05:00
Anuar Ustayev (aka Anu)
af5b6b7a29 Rename/rebrand from datahub to portaljs.
DataHub.io is becoming something different, e.g., hub for data OR data market[place] while PortalJS.com is a cloud platform for creating managed data portals.
2024-12-24 10:43:26 +05:00
muhammad-hassan11
8487175f01 [md][datahub] updated links from portaljs.org to portaljs.com 2024-12-23 21:57:09 +05:00
Anuar Ustayev (aka Anu)
6551576700 Change back to PortalJS name for data portals. 2024-12-23 11:10:39 +05:00
Lucas Morais Bispo
4fccb2945f Merge pull request #1346 from datopian/fix/dotorgmerging
[site][WIP] Seo - update title, canonical
2024-12-05 19:35:07 -03:00
lucasmbispo
a9025e5cbe [site]:seo - update title, canonical 2024-12-05 08:14:18 -03:00
github-actions[bot]
ad5a176e85 Version Packages 2024-11-11 15:52:06 +01:00
Ola Rubaj
eeb480e8cf [fix][xs]: allow yearmonth TimeUnit in LineChart 2024-11-11 15:40:07 +01:00
github-actions[bot]
30fcb256b2 Version Packages 2024-10-24 08:53:23 +02:00
Ola Rubaj
a4f8c0ed76 [chore][xs]: update package-lock 2024-10-24 08:46:51 +02:00
Ola Rubaj
829f3b1f13 [chore][xs]: fix formatting 2024-10-24 08:46:27 +02:00
Ola Rubaj
836b143a31 [fix][xs]: make tileLayerName in Map optional 2024-10-24 08:45:51 +02:00
github-actions[bot]
be38086794 Version Packages 2024-10-23 18:08:18 +02:00
Ola Rubaj
63d9e3b754 [feat,LineChart][s]: support for multiple series 2024-10-23 18:03:07 +02:00
Anuar Ustayev (aka Anu)
f86f0541eb Merge pull request #1332 from datopian/site/fix-showcases
[portaljs site][showcases][s] Merge examples into Showcases tab
2024-10-11 09:36:16 +05:00
Lucas Morais Bispo
64bc212384 Update README.md 2024-10-09 11:46:02 -03:00
Lucas Morais Bispo
1e7daf353d Add files via upload 2024-10-09 11:28:42 -03:00
lucasmbispo
cc69dabf80 [site][showcases] update examples 2024-10-03 21:04:06 -03:00
lucasmbispo
a5d87712e0 [site][showcases][s] Merge examples into Showcases tab 2024-10-01 11:07:33 -03:00
Rufus Pollock
86834fd1a6 Merge pull request #1317 from loleg/patch-1
Fix link to Next.js in README.md
2024-09-20 13:30:02 +02:00
Oleg Lavrovsky
8a661b1617 Fix link to Next.js in README.md 2024-09-20 11:23:06 +02:00
Rufus Pollock
1baebc3f3c Merge pull request #1200 from rzmk/patch-1
[#1181, examples/ckan-ssg][xs]: update example generation command
2024-07-05 19:13:43 +02:00
João Demenech
bbac4954f5 Merge pull request #1202 from datopian/changeset-release/main
Version Packages
2024-06-24 17:58:02 -03:00
github-actions[bot]
be6b184884 Version Packages 2024-06-24 20:47:23 +00:00
João Demenech
64103d6488 Merge pull request #1122 from datopian/feature/custom-tile-layer
Custom Tile Layer for Map Component
2024-06-24 17:44:19 -03:00
Demenech
8e3496782c version: add changeset 2024-06-24 17:42:49 -03:00
Mueez Khan
e034503399 [examples/ckan-ssg][xs]: update command to create project 2024-06-22 00:17:49 -04:00
William Lima
93ae498ec2 Code cleanup 2024-06-19 10:10:56 -01:00
William Lima
97e43fdcba add mapbox as default basemap 2024-06-18 22:37:20 -01:00
William Lima
32f29024f8 attr replace fix 2024-06-18 22:05:41 -01:00
William Lima
134f72948c Add TileLayer Presets configuration 2024-06-18 22:01:59 -01:00
Rufus Pollock
c1f2c526a8 [#1181,site][xs]: change portaljs to datahub in github repo references. 2024-06-10 19:31:43 +02:00
João Demenech
8feb87739d Merge pull request #1173 from datopian/changeset-release/main
Version Packages
2024-06-09 08:06:43 -03:00
github-actions[bot]
3a07267e44 Version Packages 2024-06-09 09:25:23 +00:00
Rufus Pollock
3f19ca16ed [#1118,docs/portaljs][s 2024-06-09 11:22:25 +02:00
João Demenech
5deabac5fe Merge pull request #1170 from datopian/fix/iframe-height
[components][iFrame] Change default height
2024-06-04 14:57:24 -03:00
lucasmbispo
96901150c6 [changesets] change major to patch 2024-06-04 09:38:47 -03:00
lucasmbispo
9ff25ed7c4 [components][iFrame] Change iFrame height 2024-06-04 09:38:12 -03:00
lucasmbispo
8f884fceab [components][iFrame] Change default height 2024-06-04 09:26:30 -03:00
Anuar Ustayev (aka Anu)
7094eded50 Merge pull request #1167 from datopian/fix/map-geojson
Fix: autoZoomConfiguration not working properly when the geojson parameter is passed
2024-06-04 14:06:45 +05:00
Rufus Pollock
30e7c6379f Merge pull request #1069 from marcchehab/patch-2 - Add SiteToc to MobileNav.
This PR adds the SiteToc to the MobileNav. It also fixes double type declarations in MobileNav by importing the interfaces from Nav. Adding SiteToc was then just a matter of uncommenting code that was there already.
2024-05-31 17:16:42 +02:00
Ronaldo Campos
feada58932 Fix: autoZoomConfiguration not working properly when the geojson parameter is passed 2024-05-31 11:37:01 -03:00
William Lima
31406d48e3 Update Map.tsx 2024-05-31 10:29:15 -01:00
Daniellappv
d6bf344ca3 Update CONTRIBUTING.md 2024-05-31 10:55:58 +03:00
William Lima
d1a5138c6e include configs on .env vars or pass through props 2024-05-22 11:48:20 -01:00
William Lima
a6047a9341 Implements Custom Tile Layer
#1121 adds default tile layer and allows user to pass a tile object to map
2024-05-13 12:51:28 -01:00
Ola Rubaj
a4e60540ae Merge pull request #1119 from datopian/remark-wiki-link-cleanup
## Changes

- remove unneeded tests
- do not remove "index" from the end of tile path in `getPermalinks` function
2024-05-09 02:20:45 +02:00
Ola Rubaj
e4c456c237 rm changeset file 2024-05-09 02:19:54 +02:00
Ola Rubaj
ce9ebbf41e add changeset file 2024-05-09 02:16:05 +02:00
Ola Rubaj
a8fb176bcc rm test for custom permarlink converter (irrelevant) 2024-05-09 02:12:44 +02:00
Ola Rubaj
2ac82367c5 do not remove "index" from the end of file
- should be treated as a regular file name
- it's up to the app how to interpret those paths/files later
2024-05-09 02:12:38 +02:00
Ola Rubaj
85de6f7878 replace inex.md with README.md in test fixtures 2024-05-09 02:09:52 +02:00
luzmediach
1a8e7ac06e NavMobile to use Nav interfaces and add SiteToc to sidebar 2024-01-21 12:48:10 +01:00
marcchehab
4355efe0c4 Update Nav.tsx 2024-01-21 12:36:46 +01:00
65 changed files with 1689 additions and 371 deletions

View File

@@ -4,7 +4,7 @@ title: Developer docs for contributors
## Our repository
https://github.com/datopian/portaljs
https://github.com/datopian/datahub
Structure:
@@ -17,7 +17,7 @@ Structure:
## How to contribute
You can start by checking our [issues board](https://github.com/datopian/portaljs/issues).
You can start by checking our [issues board](https://github.com/datopian/datahub/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/rTxfCutu) do discuss existing issues and to ask for help.
> Join our [Discord channel](https://discord.gg/KZSf3FG4EZ) do discuss existing issues and to ask for help.
## Nx

View File

@@ -1,31 +1,25 @@
<h1 align="center">
<a href="https://datahub.io/">
<img alt="datahub" src="http://datahub.io/datahub-cube.svg" width="146">
</a>
</h1>
<p align="center">
Bugs, issues and suggestions re DataHub Cloud ☁️ and DataHub OpenSource 🌀
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>
## DataHub
## PortalJS framework
This repo and issue tracker are for
- PortalJS 🌀 - https://www.portaljs.com/
- DataHub Cloud ☁️ - https://datahub.io/
- DataHub 🌀 - https://datahub.io/opensource
### Issues
Found a bug: 👉 https://github.com/datopian/datahub/issues/new
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/datahub/discussions
Head to the discussion forum: 👉 https://github.com/datopian/portaljs/discussions
### Chat on Discord
@@ -35,13 +29,14 @@ If you would prefer to get help via live chat check out our discord 👉
### Docs
https://datahub.io/docs
- For PortalJS go to https://www.portaljs.com/opensource
- For DataHub Cloud https://datahub.io/docs
## DataHub OpenSource 🌀
## PortalJS Cloud 🌀
DataHub 🌀 is a platform for rapidly creating rich data portal and publishing systems using a modern frontend approach. Datahub can be used to publish a single dataset or build a full-scale data catalog/portal.
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.
DataHub is built in JavaScript and React on top of the popular [Next.js](https://nextjs.com/) framework. DataHub 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.
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

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.org/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.com/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/portaljs/tree/main/examples/ckan-example
npx create-next-app <app-name> --example https://github.com/datopian/datahub/tree/main/examples/ckan-ssg
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 yo go to any one of those pages by clicking on `More info` you will see something similar to this
If you 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://frictionlessdata.io/data-packages/
[fd]: https://specs.frictionlessdata.io/data-package/
## How to use

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.org"
href="https://portaljs.com"
>
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.org"
href="https://portaljs.com"
>
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.org/docs/examples/github-backed-catalog) blog post.
You can read more about it on the [Data catalog with data on GitHub](https://portaljs.com/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.org/"
href="https://portaljs.com/"
>
🌀 PortalJS
</Link>

View File

@@ -1 +1 @@
PortalJS Learn Example - https://portaljs.org/docs
PortalJS Learn Example - https://portaljs.com/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.org/docs/examples/github-backed-catalog) blog post.
You can read more about it on the [Data catalog with data on GitHub](https://portaljs.com/docs/examples/github-backed-catalog) blog post.
## Demo

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.org" 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.com" target="_blank" className='text-xl font-medium'>🌀 PortalJS</a></p>
</div>
</div>
</footer>

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.org/_
_This data story was created by using Datopian's PortalJS framework. You can learn more about the framework by visiting https://portaljs.com/_

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.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.
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.
- .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.org'>PortalJS 🌀</a>
Built with <a href='https://portaljs.com'>PortalJS 🌀</a>
</p>
<p className="text-sm text-zinc-400 dark:text-zinc-500">
&copy; {new Date().getFullYear()} Leon Derczynski. All rights

4
package-lock.json generated
View File

@@ -49897,7 +49897,7 @@
},
"packages/components": {
"name": "@portaljs/components",
"version": "0.6.0",
"version": "1.2.0",
"dependencies": {
"@githubocto/flat-ui": "^0.14.1",
"@heroicons/react": "^2.0.17",
@@ -50383,7 +50383,7 @@
},
"packages/remark-wiki-link": {
"name": "@portaljs/remark-wiki-link",
"version": "1.1.3",
"version": "1.2.0",
"license": "MIT",
"dependencies": {
"mdast-util-to-markdown": "^1.5.0",

View File

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

View File

@@ -1,9 +1,16 @@
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,5 +1,41 @@
# @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

View File

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

View File

@@ -1,8 +1,8 @@
{
"name": "@portaljs/components",
"version": "1.0.0",
"version": "1.2.3",
"type": "module",
"description": "https://portaljs.org",
"description": "https://portaljs.com",
"keywords": [
"data portal",
"data catalog",

View File

@@ -11,7 +11,7 @@ export function Iframe({ data, style }: IframeProps) {
return (
<iframe
src={url}
style={style ?? { width: `100%`, height: `100%` }}
style={style ?? { width: `100%`, height: `600px` }}
></iframe>
);
}

View File

@@ -5,7 +5,7 @@ import loadData from '../lib/loadData';
import { Data } from '../types/properties';
type AxisType = 'quantitative' | 'temporal';
type TimeUnit = 'year' | undefined; // or ...
type TimeUnit = 'year' | 'yearmonth' | undefined; // or ...
export type LineChartProps = {
data: Omit<Data, 'csv'>;
@@ -13,9 +13,10 @@ export type LineChartProps = {
xAxis: string;
xAxisType?: AxisType;
xAxisTimeUnit?: TimeUnit;
yAxis: string;
yAxis: string | string[];
yAxisType?: AxisType;
fullWidth?: boolean;
symbol?: string;
};
export function LineChart({
@@ -26,6 +27,7 @@ export function LineChart({
xAxisTimeUnit = 'year', // TODO: defaults to undefined would probably work better... keeping it as it's for compatibility purposes
yAxis,
yAxisType = 'quantitative',
symbol,
}: LineChartProps) {
const url = data.url;
const values = data.values;
@@ -33,6 +35,7 @@ export function LineChart({
// 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',
@@ -44,8 +47,17 @@ 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',
@@ -59,9 +71,25 @@ export function LineChart({
type: xAxisType,
},
y: {
field: yAxis,
field: isMultiYAxis ? 'value' : yAxis,
type: yAxisType,
},
...(symbol
? {
color: {
field: symbol,
type: 'nominal',
},
}
: {}),
...(isMultiYAxis
? {
color: {
field: 'key',
type: 'nominal',
},
}
: {}),
},
} as any;

View File

@@ -12,8 +12,32 @@ 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;
name: string;
@@ -36,7 +60,19 @@ export type MapProps = {
};
};
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,
@@ -54,6 +90,95 @@ export function Map({
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;
@@ -100,6 +225,7 @@ export function Map({
</div>
) : (
<MapContainer
key={layersData}
center={[center.latitude, center.longitude]}
zoom={zoom}
scrollWheelZoom={false}
@@ -144,10 +270,8 @@ export function Map({
map.target.fitBounds(layerToZoomBounds);
}}
>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{tileLayerData.url && <TileLayer {...tileLayerData} />}
<LayersControl position="bottomright">
{layers.map((layer) => {
const data = layersData.find(

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@
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 }[];
values?: { [key: string]: number | string | null | undefined }[];
csv?: string;
}

View File

@@ -28,6 +28,6 @@ export const Normal: Story = {
data: {
url: 'https://app.powerbi.com/view?r=eyJrIjoiYzBmN2Q2MzYtYzE3MS00ODkxLWE5OWMtZTQ2MjBlMDljMDk4IiwidCI6Ijk1M2IwZjgzLTFjZTYtNDVjMy04MmM5LTFkODQ3ZTM3MjMzOSIsImMiOjh9',
},
style: { width: `100%`, height: `100%` },
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.org](https://portaljs.org)
**Docs:** [portaljs.org/docs](https://portaljs.org/docs)
**Official Website:** [portaljs.com](https://portaljs.com)
**Docs:** [portaljs.com/opensource](https://portaljs.com/opensource)
**GitHub:** [github.com/datopian/portaljs](https://github.com/datopian/portaljs)

View File

@@ -30,11 +30,15 @@ Must be an object with one of the following properties: `url` or `values` \n\n \
},
yAxis: {
description:
'Name of the column header or object property that represents the Y-axis on the data.',
'Name of the column headers or object properties that represent the Y-axis on the data.',
},
yAxisType: {
description: 'Type of the Y-axis',
},
symbol: {
description:
'Name of the column header or object property that represents a series for multiple series.',
},
},
};
@@ -60,6 +64,51 @@ export const FromDataPoints: Story = {
},
};
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'],
},
};
export const FromURL: Story = {
name: 'Line chart from URL',
args: {
@@ -71,3 +120,42 @@ export const FromURL: Story = {
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

@@ -43,6 +43,10 @@ type Story = StoryObj<MapProps>;
export const GeoJSONPolygons: Story = {
name: 'GeoJSON polygons map',
args: {
tileLayerName:'MapBox',
tileLayerOptions:{
accessToken : 'pk.eyJ1Ijoid2lsbHktcGFsbWFyZWpvIiwiYSI6ImNqNzk5NmRpNDFzb2cyeG9sc2luMHNjajUifQ.lkoVRFSI8hOLH4uJeOzwXw',
},
layers: [
{
data: {

View File

@@ -53,7 +53,7 @@ export const Nav: React.FC<Props> = ({
<nav className="flex justify-between">
{/* Mobile navigation */}
<div className="mr-2 sm:mr-4 flex lg:hidden">
<NavMobile links={links}>{children}</NavMobile>
<NavMobile {...{title, links, social, search, defaultTheme, themeToggleIcon}}>{children}</NavMobile>
</div>
{/* Non-mobile navigation */}
<div className="flex flex-none items-center">

View File

@@ -4,20 +4,16 @@ import { useRouter } from "next/router.js";
import { useEffect, useState } from "react";
import { SearchContext, SearchField } from "../Search";
import { MenuIcon, CloseIcon } from "../Icons";
import { NavLink, SearchProviderConfig } from "../types";
import type { NavConfig, ThemeConfig } from "./Nav";
interface Props extends React.PropsWithChildren {
author?: string;
links?: Array<NavLink>;
search?: SearchProviderConfig;
}
interface Props extends NavConfig, ThemeConfig, React.PropsWithChildren {}
// TODO why mobile navigation only accepts author and regular nav accepts different things like title, logo, version
// TODO: Search doesn't appear
export const NavMobile: React.FC<Props> = ({
children,
title,
links,
search,
author,
}) => {
const router = useRouter();
const [isOpen, setIsOpen] = useState(false);
@@ -77,8 +73,8 @@ export const NavMobile: React.FC<Props> = ({
legacyBehavior
>
{/* <Logomark className="h-9 w-9" /> */}
<div className="font-extrabold text-primary dark:text-primary-dark text-2xl ml-6">
{author}
<div className="font-extrabold text-primary dark:text-primary-dark text-lg ml-6">
{title}
</div>
</Link>
</div>
@@ -106,9 +102,7 @@ export const NavMobile: React.FC<Props> = ({
))}
</ul>
)}
{/* <div className="pt-6 border border-t-2">
{children}
</div> */}
<div className="pt-6">{children}</div>
</Dialog.Panel>
</Dialog>
</>

View File

@@ -38,6 +38,5 @@ const defaultPathToPermalinkFunc = (
.replace(markdownFolder, "") // make the permalink relative to the markdown folder
.replace(/\.(mdx|md)/, "")
.replace(/\\/g, "/") // replace windows backslash with forward slash
.replace(/\/index$/, ""); // remove index from the end of the permalink
return permalink.length > 0 ? permalink : "/"; // for home page
};

View File

@@ -1,9 +1,6 @@
import * as path from "path";
// import * as url from "url";
import { getPermalinks } from "../src/utils";
// const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
// const markdownFolder = path.join(__dirname, "/fixtures/content");
const markdownFolder = path.join(
".",
"test/fixtures/content"
@@ -12,12 +9,12 @@ const markdownFolder = path.join(
describe("getPermalinks", () => {
test("should return an array of permalinks", () => {
const expectedPermalinks = [
"/", // /index.md
"/README",
"/abc",
"/blog/first-post",
"/blog/Second Post",
"/blog/third-post",
"/blog", // /blog/index.md
"/blog/README",
"/blog/tutorials/first-tutorial",
"/assets/Pasted Image 123.png",
];
@@ -28,35 +25,4 @@ describe("getPermalinks", () => {
expect(expectedPermalinks).toContain(permalink);
});
});
test("should return an array of permalinks with custom path -> permalink converter function", () => {
const expectedPermalinks = [
"/", // /index.md
"/abc",
"/blog/first-post",
"/blog/second-post",
"/blog/third-post",
"/blog", // /blog/index.md
"/blog/tutorials/first-tutorial",
"/assets/pasted-image-123.png",
];
const func = (filePath: string, markdownFolder: string) => {
const permalink = filePath
.replace(markdownFolder, "") // make the permalink relative to the markdown folder
.replace(/\.(mdx|md)/, "")
.replace(/\\/g, "/") // replace windows backslash with forward slash
.replace(/\/index$/, "") // remove index from the end of the permalink
.replace(/ /g, "-") // replace spaces with hyphens
.toLowerCase(); // convert to lowercase
return permalink.length > 0 ? permalink : "/"; // for home page
};
const permalinks = getPermalinks(markdownFolder, [/\.DS_Store/], func);
expect(permalinks).toHaveLength(expectedPermalinks.length);
permalinks.forEach((permalink) => {
expect(expectedPermalinks).toContain(permalink);
});
});
});

View File

@@ -286,56 +286,6 @@ describe("micromark-extension-wiki-link", () => {
});
});
test("parses wiki links to index files", () => {
const serialized = micromark("[[/some/folder/index]]", "ascii", {
extensions: [syntax()],
htmlExtensions: [html() as any], // TODO type fix
});
expect(serialized).toBe(
'<p><a href="/some/folder" class="internal new">/some/folder/index</a></p>'
);
});
describe("other", () => {
test("parses a wiki link to some index page in a folder with no matching permalink", () => {
const serialized = micromark("[[/some/folder/index]]", "ascii", {
extensions: [syntax()],
htmlExtensions: [html() as any], // TODO type fix
});
expect(serialized).toBe(
'<p><a href="/some/folder" class="internal new">/some/folder/index</a></p>'
);
});
test("parses a wiki link to some index page in a folder with a matching permalink", () => {
const serialized = micromark("[[/some/folder/index]]", "ascii", {
extensions: [syntax()],
htmlExtensions: [html({ permalinks: ["/some/folder"] }) as any], // TODO type fix
});
expect(serialized).toBe(
'<p><a href="/some/folder" class="internal">/some/folder/index</a></p>'
);
});
test("parses a wiki link to home index page with no matching permalink", () => {
const serialized = micromark("[[/index]]", "ascii", {
extensions: [syntax()],
htmlExtensions: [html() as any], // TODO type fix
});
expect(serialized).toBe(
'<p><a href="/" class="internal new">/index</a></p>'
);
});
test("parses a wiki link to home index page with a matching permalink", () => {
const serialized = micromark("[[/index]]", "ascii", {
extensions: [syntax()],
htmlExtensions: [html({ permalinks: ["/"] }) as any], // TODO type fix
});
expect(serialized).toBe('<p><a href="/" class="internal">/index</a></p>');
});
});
describe("transclusions", () => {
test("parsers a transclusion as a regular wiki link", () => {
const serialized = micromark("![[Some Page]]", "ascii", {

View File

@@ -485,109 +485,6 @@ describe("remark-wiki-link", () => {
});
});
test("parses wiki links to index files", () => {
const processor = unified().use(markdown).use(wikiLinkPlugin);
let ast = processor.parse("[[/some/folder/index]]");
ast = processor.runSync(ast);
expect(select("wikiLink", ast)).not.toEqual(null);
visit(ast, "wikiLink", (node: Node) => {
expect(node.data?.exists).toEqual(false);
expect(node.data?.permalink).toEqual("/some/folder");
expect(node.data?.alias).toEqual(null);
expect(node.data?.hName).toEqual("a");
expect((node.data?.hProperties as any).className).toEqual("internal new");
expect((node.data?.hProperties as any).href).toEqual("/some/folder");
expect((node.data?.hChildren as any)[0].value).toEqual(
"/some/folder/index"
);
});
});
describe("other", () => {
test("parses a wiki link to some index page in a folder with no matching permalink", () => {
const processor = unified().use(markdown).use(wikiLinkPlugin);
let ast = processor.parse("[[/some/folder/index]]");
ast = processor.runSync(ast);
visit(ast, "wikiLink", (node: Node) => {
expect(node.data?.exists).toEqual(false);
expect(node.data?.permalink).toEqual("/some/folder");
expect(node.data?.alias).toEqual(null);
expect(node.data?.hName).toEqual("a");
expect((node.data?.hProperties as any).className).toEqual(
"internal new"
);
expect((node.data?.hProperties as any).href).toEqual("/some/folder");
expect((node.data?.hChildren as any)[0].value).toEqual(
"/some/folder/index"
);
});
});
test("parses a wiki link to some index page in a folder with a matching permalink", () => {
const processor = unified()
.use(markdown)
.use(wikiLinkPlugin, { permalinks: ["/some/folder"] });
let ast = processor.parse("[[/some/folder/index]]");
ast = processor.runSync(ast);
visit(ast, "wikiLink", (node: Node) => {
expect(node.data?.exists).toEqual(true);
expect(node.data?.permalink).toEqual("/some/folder");
expect(node.data?.alias).toEqual(null);
expect(node.data?.hName).toEqual("a");
expect((node.data?.hProperties as any).className).toEqual("internal");
expect((node.data?.hProperties as any).href).toEqual("/some/folder");
expect((node.data?.hChildren as any)[0].value).toEqual(
"/some/folder/index"
);
});
});
test("parses a wiki link to home index page with no matching permalink", () => {
const processor = unified().use(markdown).use(wikiLinkPlugin);
let ast = processor.parse("[[/index]]");
ast = processor.runSync(ast);
visit(ast, "wikiLink", (node: Node) => {
expect(node.data?.exists).toEqual(false);
expect(node.data?.permalink).toEqual("/");
expect(node.data?.alias).toEqual(null);
expect(node.data?.hName).toEqual("a");
expect((node.data?.hProperties as any).className).toEqual(
"internal new"
);
expect((node.data?.hProperties as any).href).toEqual("/");
expect((node.data?.hChildren as any)[0].value).toEqual("/index");
});
});
test("parses a wiki link to home index page with a matching permalink", () => {
const processor = unified()
.use(markdown)
.use(wikiLinkPlugin, { permalinks: ["/"] });
let ast = processor.parse("[[/index]]");
ast = processor.runSync(ast);
visit(ast, "wikiLink", (node: Node) => {
expect(node.data?.exists).toEqual(true);
expect(node.data?.permalink).toEqual("/");
expect(node.data?.alias).toEqual(null);
expect(node.data?.hName).toEqual("a");
expect((node.data?.hProperties as any).className).toEqual("internal");
expect((node.data?.hProperties as any).href).toEqual("/");
expect((node.data?.hChildren as any)[0].value).toEqual("/index");
});
});
});
describe("transclusions", () => {
test("replaces a transclusion with a regular wiki link", () => {
const processor = unified().use(markdown).use(wikiLinkPlugin);

View File

@@ -12,7 +12,7 @@ export default function JSONLD({
return <></>;
}
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://portaljs.org';
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://portaljs.com';
const pageUrl = `${baseUrl}/${meta.urlPath}`;
const imageMatches = source.match(

View File

@@ -81,7 +81,6 @@ export default function Layout({
}
return section.children.findIndex(isActive) > -1;
}
return (
<>
{title && <NextSeo title={title} description={description} />}

View File

@@ -22,11 +22,41 @@ const items = [
sourceUrl: 'https://github.com/FCSCOpendata/frontend',
},
{
title: 'Datahub Open Data',
href: 'https://opendata.datahub.io/',
image: '/images/showcases/datahub.webp',
description: 'Demo Data Portal by DataHub',
title: 'Frictionless Data',
href: 'https://datahub.io/core/co2-ppm',
repository: 'https://github.com/datopian/datahub/tree/main/examples/dataset-frictionless',
image: '/images/showcases/frictionless-capture.png',
description: 'Progressive open-source framework for building data infrastructure - data management, data integration, data flows, etc. It includes various data standards and provides software to work with data.',
},
{
title: "OpenSpending",
image: "/images/showcases/openspending.png",
href: "https://www.openspending.org",
repository: 'https://github.com/datopian/datahub/tree/main/examples/openspending',
description: "OpenSpending is a free, open and global platform to search, visualise and analyse fiscal data in the public sphere."
},
{
title: "FiveThirtyEight",
image: "/images/showcases/fivethirtyeight.png",
href: "https://fivethirtyeight.portaljs.org/",
repository: 'https://github.com/datopian/datahub/tree/main/examples/fivethirtyeight',
description: "This is a replica of data.fivethirtyeight.com using PortalJS."
},
{
title: "Github Datasets",
image: "/images/showcases/github-datasets.png",
href: "https://example.portaljs.org/",
repository: 'https://github.com/datopian/datahub/tree/main/examples/github-backed-catalog',
description: "A simple data catalog that get its data from a list of GitHub repos that serve as datasets."
},
{
title: "Hatespeech Data",
image: "/images/showcases/turing.png",
href: "https://hatespeechdata.com/",
repository: 'https://github.com/datopian/datahub/tree/main/examples/turing',
description: "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."
},
];
export default function Showcases() {

View File

@@ -1,10 +1,6 @@
export default function ShowcasesItem({ item }) {
return (
<a
className="rounded overflow-hidden group relative border-1 shadow-lg"
target="_blank"
href={item.href}
>
<div className="rounded overflow-hidden group relative border-1 shadow-lg">
<div
className="bg-cover bg-no-repeat bg-top aspect-video w-full group-hover:blur-sm group-hover:scale-105 transition-all duration-200"
style={{ backgroundImage: `url(${item.image})` }}
@@ -16,9 +12,48 @@ export default function ShowcasesItem({ item }) {
<div className="text-center text-primary-dark">
<span className="text-xl font-semibold">{item.title}</span>
<p className="text-base font-medium">{item.description}</p>
</div>
</div>
</div>
<div className="flex justify-center mt-2 gap-2 ">
{item.href && (
<a
target="_blank"
className=" text-white w-8 h-8 p-1 bg-primary rounded-full hover:scale-110 transition cursor-pointer z-50"
rel="noreferrer"
href={item.href}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 420 420"
stroke="white"
fill="none"
>
<path stroke-width="26" d="M209,15a195,195 0 1,0 2,0z" />
<path
stroke-width="18"
d="m210,15v390m195-195H15M59,90a260,260 0 0,0 302,0 m0,240 a260,260 0 0,0-302,0M195,20a250,250 0 0,0 0,382 m30,0 a250,250 0 0,0 0-382"
/>
</svg>
</a>
)}
{item.repository && (
<a
target="_blank"
rel="noreferrer"
className="w-8 h-8 bg-black rounded-full p-1 hover:scale-110 transition cursor-pointer z-50"
href={item.repository}
>
<svg
aria-hidden="true"
viewBox="0 0 16 16"
fill="currentColor"
>
<path d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z" />
</svg>
</a>
)}
</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -7,17 +7,17 @@ filetype: 'blog'
This post walks you though adding maps and geospatial visualizations to PortalJS.
Are you interested in building rich and interactive data portals? Do you find value in the power and flexibility of JavaScript, Nextjs, and React? If so, [PortalJS](https://portaljs.org/) is for you. It's a state-of-the-art framework leveraging these technologies to help you build rich data portals.
Are you interested in building rich and interactive data portals? Do you find value in the power and flexibility of JavaScript, Nextjs, and React? If so, [PortalJS](https://portaljs.com/) is for you. It's a state-of-the-art framework leveraging these technologies to help you build rich data portals.
Effective data visualization lies in the use of various data components. Within [PortalJS](https://portaljs.org/), we take data visualization a step further. It's not just about displaying data - it's about telling a story through combining a variety of data components.
Effective data visualization lies in the use of various data components. Within [PortalJS](https://portaljs.com/), we take data visualization a step further. It's not just about displaying data - it's about telling a story through combining a variety of data components.
In this post we will share our latest enhancement to PortalJS: maps, a powerful tool for visualizing geospatial data. In this post, we will to take you on a tour of our experiments and progress in enhancing map functionalities on PortalJS. The journey is still in its early stages, with new facets being unveiled and refined as we perfect our API.
## Exploring Map Formats
Maps play a crucial role in geospatial data visualization. Several formats exist for storing and sharing this type of data, with GeoJSON, KML, and shapefiles being among the most popular. As a prominent figure in the field of open-source data portal platforms, [PortalJS](https://portaljs.org/) strives to support as many map formats as possible.
Maps play a crucial role in geospatial data visualization. Several formats exist for storing and sharing this type of data, with GeoJSON, KML, and shapefiles being among the most popular. As a prominent figure in the field of open-source data portal platforms, [PortalJS](https://portaljs.com/) strives to support as many map formats as possible.
Taking inspiration from the ckanext-geoview extension, we currently support KML and GeoJSON formats in [PortalJS](https://portaljs.org/). This remarkable extension is a plugin for CKAN, the worlds leading open source data management system, that enables users to visualize geospatial data in diverse formats on an interactive map. Apart from KML and GeoJSON formats support, our roadmap entails extending compatibility to encompass all other formats supported by ckanext-geoview. Rest assured, we are committed to empowering users with a wide array of map format options in the future.
Taking inspiration from the ckanext-geoview extension, we currently support KML and GeoJSON formats in [PortalJS](https://portaljs.com/). This remarkable extension is a plugin for CKAN, the worlds leading open source data management system, that enables users to visualize geospatial data in diverse formats on an interactive map. Apart from KML and GeoJSON formats support, our roadmap entails extending compatibility to encompass all other formats supported by ckanext-geoview. Rest assured, we are committed to empowering users with a wide array of map format options in the future.
So, what makes these formats special?
@@ -27,7 +27,7 @@ So, what makes these formats special?
## Unveiling the Power of Leaflet and OpenLayers
To display maps in [PortalJS](https://portaljs.org/), we utilize two powerful JavaScript libraries for creating interactive maps based on different layers: Leaflet and OpenLayers. Each offers distinct advantages (and disadvantages), inspiring us to integrate both and give users the flexibility to choose.
To display maps in [PortalJS](https://portaljs.com/), we utilize two powerful JavaScript libraries for creating interactive maps based on different layers: Leaflet and OpenLayers. Each offers distinct advantages (and disadvantages), inspiring us to integrate both and give users the flexibility to choose.
Leaflet is the leading open-source JavaScript library known for its mobile-friendly, interactive maps. With its compact size (just 42 KB of JS), it provides all the map features most developers need. Leaflet is designed with simplicity, performance and usability in mind. It works efficiently across all major desktop and mobile platforms.
@@ -59,8 +59,8 @@ Users can also choose a region of focus, which will depend on the data, by setti
Through our ongoing enhancements to the [PortalJS library](https://storybook.portaljs.org/), we aim to empower users to create engaging and informative data portals featuring diverse map formats and data components.
Why not give [PortalJS](https://portaljs.org/) a try today and discover the possibilities for your own data portals? To get started, check out our comprehensive documentation here: [PortalJS Documentation](https://portaljs.org/docs).
Why not give [PortalJS](https://portaljs.com/) a try today and discover the possibilities for your own data portals? To get started, check out our comprehensive documentation here: [PortalJS Documentation](https://portaljs.com/opensource).
Have questions or comments about using [PortalJS](https://portaljs.org/) for your data portals? Feel free to share your thoughts on our [Discord channel](https://discord.com/invite/EeyfGrGu4U). We're here to help you make the most of your data.
Have questions or comments about using [PortalJS](https://portaljs.com/) for your data portals? Feel free to share your thoughts on our [Discord channel](https://discord.com/invite/EeyfGrGu4U). We're here to help you make the most of your data.
Stay tuned for more exciting developments as we continue to enhance [PortalJS](https://portaljs.org/)!
Stay tuned for more exciting developments as we continue to enhance [PortalJS](https://portaljs.com/)!

View File

@@ -4,7 +4,7 @@ authors: ['Luccas Mateus']
date: 2021-04-20
---
We have created a full data portal demo using PortalJS all backed by a CKAN instance storing data and metadata, you can see below a screenshot of the homepage and of an individual dataset page.
We have created a full data portal demo using DataHub PortalJS all backed by a CKAN instance storing data and metadata, you can see below a screenshot of the homepage and of an individual dataset page.
![](https://i.imgur.com/ai0VLS4.png)
![](https://i.imgur.com/3RhXOW4.png)
@@ -14,7 +14,7 @@ We have created a full data portal demo using PortalJS all backed by a CKAN inst
To create a Portal app, run the following command in your terminal:
```console
npx create-next-app -e https://github.com/datopian/portaljs/tree/main/examples/ckan
npx create-next-app -e https://github.com/datopian/datahub/tree/main/examples/ckan
```
> NB: Under the hood, this uses the tool called create-next-app, which bootstraps an app for you based on our CKAN example.

View File

@@ -30,12 +30,12 @@ https://github.com/datopian/markdowndb
## 📚 The Guide
https://portaljs.org/guide
https://portaljs.com/opensource
Ive sketched overviews for two upcoming tutorials:
1. **Collaborating with others on your website**: Learn how to make your website projects a team effort. [See it here](https://portaljs.org/guide#tutorial-3-collaborating-with-others-on-your-website-project)
2. **Customising your website and previewing your changes locally**: Customize and preview your site changes locally, without headaches. [See it here](https://portaljs.org/guide#tutorial-4-customising-your-website-locally-and-previewing-your-changes-locally)
1. **Collaborating with others on your website**: Learn how to make your website projects a team effort. [See it here](https://portaljs.com/guide#tutorial-3-collaborating-with-others-on-your-website-project)
2. **Customising your website and previewing your changes locally**: Customize and preview your site changes locally, without headaches. [See it here](https://portaljs.com/guide#tutorial-4-customising-your-website-locally-and-previewing-your-changes-locally)
## 🌐 LifeItself.org

View File

@@ -11,7 +11,7 @@ In our last article, we explored [the Open Spending revamp](https://www.datopian
## The Core: PortalJS
At the core of the revamped OpenSpending website is [PortalJS](https://portaljs.org), a JavaScript library that's a game-changer in building powerful data portals with data visualizations. What makes it so special? Well, it's packed with reusable React components that make our lives - and yours - a whole lot easier. Take, for example, our sleek CSV previews; they're brought to life by PortalJS' [FlatUI Component](https://storybook.portaljs.org/?path=/story/components-flatuitable--from-url). It helps transform raw numbers into visuals that you can easily understand and use. Curious to know more? Check out the [official PortalJS website](https://portaljs.org).
At the core of the revamped OpenSpending website is [PortalJS](https://portaljs.com), a JavaScript library that's a game-changer in building powerful data portals with data visualizations. What makes it so special? Well, it's packed with reusable React components that make our lives - and yours - a whole lot easier. Take, for example, our sleek CSV previews; they're brought to life by PortalJS' [FlatUI Component](https://storybook.portaljs.org/?path=/story/components-flatuitable--from-url). It helps transform raw numbers into visuals that you can easily understand and use. Curious to know more? Check out the [official PortalJS website](https://portaljs.com).
![Data visualization](/assets/blog/2023-10-13-the-open-spending-revamp-behind-the-scenes/data-visualization.png)

View File

@@ -11,19 +11,18 @@ const config = {
authorUrl: 'https://datopian.com/',
navbarTitle: {
// logo: "/images/logo.svg",
text: '🌀 PortalJS',
text: '🌀 DataHub PortalJS',
// version: "Alpha",
},
navLinks: [
{ name: 'Docs', href: '/docs' },
// { name: "Components", href: "/docs/components" },
{ name: 'Blog', href: '/blog' },
{ name: 'Showcases', href: '/#showcases' },
{ name: 'Howtos', href: '/howtos' },
{ name: 'Guide', href: '/guide' },
{
name: 'Examples',
href: '/examples/'
name: 'Showcases',
href: '/showcases/'
},
{
name: 'Components',
@@ -45,6 +44,7 @@ const config = {
{ rel: 'icon', href: '/favicon.ico' },
{ rel: 'apple-touch-icon', href: '/icon.png', sizes: '120x120' },
],
canonical: 'https://portaljs.com/',
openGraph: {
type: 'website',
title:
@@ -68,8 +68,8 @@ const config = {
cardType: 'summary_large_image',
},
},
github: 'https://github.com/datopian/portaljs',
discord: 'https://discord.gg/xfFDMPU9dC',
github: 'https://github.com/datopian/datahub',
discord: 'https://discord.gg/KrRzMKU',
tableOfContents: true,
analytics: 'G-96GWZHMH57',
// editLinkShow: true,

View File

@@ -26,7 +26,7 @@ Below are some screenshots:
- Create a new app with `create-next-app`:
```
npx create-next-app <app-name> --example https://github.com/datopian/portaljs/tree/main/examples/ckan-example
npx create-next-app <app-name> --example https://github.com/datopian/datahub/tree/main/examples/ckan-example
cd <app-name>
```
@@ -49,7 +49,7 @@ If yo go to any one of those pages by clicking on `More info` you will see somet
## Deployment
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdatopian%2Fportaljs%2Ftree%2Fmain%2Fexamples%2Fckan-example&env=DMS&envDescription=URL%20For%20the%20CKAN%20Backend%20Ex%3A%20https%3A%2F%2Fdemo.dev.datopian.com)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdatopian%2Fdatahub%2Ftree%2Fmain%2Fexamples%2Fckan-example&env=DMS&envDescription=URL%20For%20the%20CKAN%20Backend%20Ex%3A%20https%3A%2F%2Fdemo.dev.datopian.com)
By clicking on this button, you will be redirected to a page which will allow you to clone the content into your own github/gitlab/bitbucket account and automatically deploy everything.
@@ -70,6 +70,6 @@ npm run start
## Links
- [Repo](https://github.com/datopian/portaljs/tree/main/examples/ckan-example)
- [Repo](https://github.com/datopian/datahub/tree/main/examples/ckan-example)
- [Live Demo](https://ckan-example.portaljs.org)

View File

@@ -26,7 +26,7 @@ To get a feel of the project, check out the demo at [live deployment](https://ck
Navigate to the directory in which you want to create the project folder and run the following command:
```
npx create-next-app <app-name> --example https://github.com/datopian/portaljs/tree/main/examples/ckan
npx create-next-app <app-name> --example https://github.com/datopian/datahub/tree/main/examples/ckan
cd <app-name>
```
@@ -56,7 +56,7 @@ If you navigate to any of the dataset pages by clicking on the dataset title you
## Deployment
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdatopian%2Fportaljs%2Ftree%2Fmain%2Fexamples%2Fckan&env=DMS&envDescription=URL%20For%20the%20CKAN%20Backend%20Ex%3A%20https%3A%2F%2Fdemo.dev.datopian.com)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdatopian%2Fdatahub%2Ftree%2Fmain%2Fexamples%2Fckan&env=DMS&envDescription=URL%20For%20the%20CKAN%20Backend%20Ex%3A%20https%3A%2F%2Fdemo.dev.datopian.com)
By clicking on this button, you will be redirected to a page which allows you to clone the base project into your own GitHub/GitLab/BitBucket account and automatically deploy it.
@@ -158,6 +158,6 @@ Thanks to TypeScript, you can get a list of all the API methods in `@portaljs/ck
## Links
- [Repo](https://github.com/datopian/portaljs/tree/main/examples/ckan)
- [Repo](https://github.com/datopian/datahub/tree/main/examples/ckan)
- [Live Demo](http://ckan.portaljs.org/)

View File

@@ -1,48 +0,0 @@
---
title: "Example: showcase for a single Frictionless dataset"
authors: ['Luccas Mateus']
date: 2023-04-20
filetype: blog
---
**See the repo:** https://github.com/datopian/portaljs/tree/main/examples/dataset-frictionless
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://frictionlessdata.io/data-packages/
## How to use
```bash
npx create-next-app -e https://github.com/datopian/portaljs/tree/main/examples/dataset-frictionless
# choose a name for your portal when prompted e.g. your-portal or go with default my-app
# then run it
cd your-portal
yarn #install packages
yarn dev #start app in dev mode
```
You should see the demo portal running with the example dataset provided:
<img src="/assets/examples/frictionless-dataset-demo.gif" />
### Use your own dataset
You can try it out with other [Frictionless datasets](https://datahub.io/search).
In the directory of your portal do:
```bash
export PORTAL_DATASET_PATH=/path/to/my/dataset
```
Then restart the dev server:
```
yarn dev
```
Check the portal page and it should have updated e.g. like:
![](https://i.imgur.com/KSEtNF1.png)

View File

@@ -33,7 +33,7 @@ Run the following commands:
```bash
npx create-next-app <app-name> --example https://github.com/datopian/portaljs/tree/main/examples/github-backed-catalog
npx create-next-app <app-name> --example https://github.com/datopian/datahub/tree/main/examples/github-backed-catalog
cd <app-name>
```
@@ -61,7 +61,7 @@ Congratulations, your new app is now running at http://localhost:3000.
## Deployment
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdatopian%2Fportaljs%2Ftree%2Fmain%2Fexamples%2Fgithub-backed-catalog)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdatopian%2Fdatahub%2Ftree%2Fmain%2Fexamples%2Fgithub-backed-catalog)
By clicking on this button, you will be redirected to a page which will allow you to clone the example into your own GitHub/GitLab/BitBucket account and automatically deploy it.
@@ -119,5 +119,5 @@ npm run start
## Links
- [Repo](https://github.com/datopian/portaljs/tree/main/examples/github-backed-catalog)
- [Repo](https://github.com/datopian/datahub/tree/main/examples/github-backed-catalog)
- [Live Demo](https://example.portaljs.org)

View File

@@ -3,9 +3,9 @@ title: Getting Started
description: 'Getting started guide and tutorial about data portal-building with PortalJS!'
---
Welcome to the PortalJS documentation!
Welcome to the DataHub PortalJS documentation!
If you have questions about anything related to PortalJS, you're always welcome to ask our community on [GitHub Discussions](https://github.com/datopian/portaljs/discussions) or on [our chat channel on Discord](https://discord.gg/EeyfGrGu4U).
If you have questions about anything related to PortalJS, you're always welcome to ask our community on [GitHub Discussions](https://github.com/datopian/datahub/discussions) or on [our chat channel on Discord](https://discord.com/invite/KrRzMKU).
## Setup
@@ -16,10 +16,10 @@ If you have questions about anything related to PortalJS, you're always welcome
### Create a PortalJS app
To create a PortalJS app, open your terminal, cd into the directory youd like to create the app in, and run the following command:
To create a DataHub PortalJS app, open your terminal, cd into the directory youd like to create the app in, and run the following command:
```bash
npx create-next-app my-data-portal --example https://github.com/datopian/portaljs/tree/main/examples/learn
npx create-next-app my-data-portal --example https://github.com/datopian/datahub/tree/main/examples/learn
```
> [!tip]

View File

@@ -1,5 +0,0 @@
# Examples
For now, see the examples folder in github:
https://github.com/datopian/portaljs/tree/main/examples

View File

@@ -29,5 +29,5 @@ It would be too complicated (and long) to explain all of the formatting aspects
## Other useful pages
[How to quickly add a simple Markdown-based page](https://guide.portaljs.org/guides/add-a-simple-md-page)
[How to quickly edit text content on a single Markdown-based page](https://guide.portaljs.org/guides/edit-text-on-a-single-md-page)
[How to quickly add a simple Markdown-based page](https://www.portaljs.com/opensource/howtos/markdown)
[How to quickly edit text content on a single Markdown-based page](https://www.portaljs.com/opensource/howtos/markdown)

View File

@@ -11,5 +11,5 @@ description: Learn more about how you can achieve different data portal features
- [[howtos/drd|How to create data-rich documents with charts and tables?]]
- [[howtos/comments|How to add user comments?]]
If you have questions about anything related to PortalJS, you're always welcome to ask our community on [GitHub Discussions](https://github.com/datopian/portaljs/discussions) or on [our chat channel on Discord](https://discord.gg/EeyfGrGu4U).
If you have questions about anything related to PortalJS, you're always welcome to ask our community on [GitHub Discussions](https://github.com/datopian/datahub/discussions) or on [our chat channel on Discord](https://discord.gg/EeyfGrGu4U).

View File

@@ -1,6 +1,6 @@
/** @type {import('next-sitemap').IConfig} */
module.exports = {
siteUrl: process.env.SITE_URL || 'https://portaljs.org',
siteUrl: process.env.SITE_URL || 'https://portaljs.com',
generateRobotsTxt: true,
robotsTxtOptions: {
policies: [

View File

@@ -50,7 +50,7 @@ function MyApp({ Component, pageProps }) {
<DefaultSeo
defaultTitle={siteConfig.title}
description={siteConfig.description}
titleTemplate="PortalJS - %s"
titleTemplate="DataHub PortalJS - %s"
{...siteConfig.nextSeo}
/>

View File

@@ -26,8 +26,8 @@ export default function Home({ sidebarTree }) {
return (
<>
<LogoJsonLd
url="https://portaljs.org"
logo="https://portaljs.org/icon.png"
url="https://portaljs.com"
logo="https://portaljs.com/icon.png"
/>
<Layout
isHomePage={true}
@@ -35,7 +35,7 @@ export default function Home({ sidebarTree }) {
sidebarTree={sidebarTree}
>
<Features />
<Showcases />
<Community />
</Layout>
</>

8
site/pages/showcases.tsx Normal file
View File

@@ -0,0 +1,8 @@
import Layout from "@/components/Layout";
import Showcases from "@/components/Showcases";
export default function ShowcasesList() {
return (
<Layout><Showcases/></Layout>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

@@ -1,7 +1,7 @@
#!/bin/bash
rm -rf portal
mkdir -p portal
npx create-next-app portal -e https://github.com/datopian/portaljs/tree/main/examples/dataset-frictionless
npx create-next-app portal -e https://github.com/datopian/datahub/tree/main/examples/dataset-frictionless
mkdir portal/public/dataset
cp -a ./data portal/public/dataset
@@ -12,7 +12,7 @@ PORTAL_DATASET_PATH=$PWD"/portal/public/dataset"
export PORTAL_DATASET_PATH
mkdir -p .github && mkdir -p .github/workflows && touch .github/workflows/main.yml
curl https://raw.githubusercontent.com/datopian/portaljs/main/site/public/scripts/gh-page-builder-action.yml > .github/workflows/main.yml
curl https://raw.githubusercontent.com/datopian/datahub/main/site/public/scripts/gh-page-builder-action.yml > .github/workflows/main.yml
cd portal
assetPrefix='"/'$PORTAL_REPO_NAME'/"'

View File

@@ -3,7 +3,7 @@ git checkout -b gh-pages
git rm -r --cached .
rm -rf portal
mkdir -p portal
npx create-next-app portal -e https://github.com/datopian/portaljs/tree/main/examples/dataset-frictionless
npx create-next-app portal -e https://github.com/datopian/datahub/tree/main/examples/dataset-frictionless
mkdir portal/public/dataset
cp -a ./data portal/public/dataset