From 3d195be8eca5f6bed33de0610f50d9cdc747e9fc Mon Sep 17 00:00:00 2001 From: Rufus Pollock Date: Wed, 5 May 2021 08:21:15 +0200 Subject: [PATCH 1/2] [scripts/README][xs]: fix typo. --- scripts/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/README.md b/scripts/README.md index 9902f7f3..116bc792 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -1,6 +1,6 @@ # Publishing data on Github using Portal.js and Github pages -Use case: you have some data in a Github repo and you'd like to publish it online using "portal" so that it is easy for others to view, explore and use it. +Use case: you have some data in a Github repo and you'd like to publish it online using "portal" so that it is easy for others to view, explore and use. Here we show how you can use portal.js plus github actions to publish your dataset in minutes and keep it updated as you make changes. From 8766c14455a413bfc6038c159bd31c03c595e31d Mon Sep 17 00:00:00 2001 From: Rising Odegua Date: Wed, 5 May 2021 09:41:24 +0100 Subject: [PATCH 2/2] [Dist][s]: Remove dist from gitignore --- .gitignore | 3 - dist/index.cjs.js | 620 ++++++++++++++++++++++++++++++++++++++++++++++ dist/index.esm.js | 575 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1195 insertions(+), 3 deletions(-) create mode 100644 dist/index.cjs.js create mode 100644 dist/index.esm.js diff --git a/.gitignore b/.gitignore index b31ce33d..b5ccbcc0 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,3 @@ yarn-error.log* .env.development.local .env.test.local .env.production.local - -# output -dist/* \ No newline at end of file diff --git a/dist/index.cjs.js b/dist/index.cjs.js new file mode 100644 index 00000000..484cf028 --- /dev/null +++ b/dist/index.cjs.js @@ -0,0 +1,620 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var React = require('react'); +var dataGrid = require('@material-ui/data-grid'); +var PropTypes = require('prop-types'); +var createPlotlyComponent = require('react-plotly.js/factory'); +var filesize = require('filesize'); +var Link = require('next/link'); +var parse = require('html-react-parser'); + +function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } + +function _interopNamespace(e) { + if (e && e.__esModule) return e; + var n = Object.create(null); + if (e) { + Object.keys(e).forEach(function (k) { + if (k !== 'default') { + var d = Object.getOwnPropertyDescriptor(e, k); + Object.defineProperty(n, k, d.get ? d : { + enumerable: true, + get: function () { + return e[k]; + } + }); + } + }); + } + n['default'] = e; + return Object.freeze(n); +} + +var React__default = /*#__PURE__*/_interopDefaultLegacy(React); +var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes); +var createPlotlyComponent__default = /*#__PURE__*/_interopDefaultLegacy(createPlotlyComponent); +var filesize__default = /*#__PURE__*/_interopDefaultLegacy(filesize); +var Link__default = /*#__PURE__*/_interopDefaultLegacy(Link); +var parse__default = /*#__PURE__*/_interopDefaultLegacy(parse); + +/** + * Displays dataset in tabular form using data grid + * @param columns: An array of column names with properties: e.g [{field: "col1", headerName: "col1"}, {field: "col2", headerName: "col2"}] + * @param data: an array of data objects e.g. [ {col1: 1, col2: 2}, {col1: 5, col2: 7} ] + */ + +var Table = function Table(_ref) { + var columns = _ref.columns, + data = _ref.data; + return /*#__PURE__*/React__default['default'].createElement("div", { + "data-testid": "tableGrid", + style: { + height: 400, + width: '100%' + } + }, /*#__PURE__*/React__default['default'].createElement(dataGrid.DataGrid, { + rows: data, + columns: columns, + pageSize: 5 + })); +}; + +Table.propTypes = { + columns: PropTypes__default['default'].array.isRequired, + data: PropTypes__default['default'].array.isRequired +}; + +function _extends() { + _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + return _extends.apply(this, arguments); +} + +function _slicedToArray(arr, i) { + return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); +} + +function _arrayWithHoles(arr) { + if (Array.isArray(arr)) return arr; +} + +function _iterableToArrayLimit(arr, i) { + var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); + + if (_i == null) return; + var _arr = []; + var _n = true; + var _d = false; + + var _s, _e; + + try { + for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { + _arr.push(_s.value); + + if (i && _arr.length === i) break; + } + } catch (err) { + _d = true; + _e = err; + } finally { + try { + if (!_n && _i["return"] != null) _i["return"](); + } finally { + if (_d) throw _e; + } + } + + return _arr; +} + +function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); +} + +function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + + return arr2; +} + +function _nonIterableRest() { + throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +} + +var Plot; + +var PlotlyChart = function PlotlyChart(_ref) { + var spec = _ref.spec; + + var _useState = React.useState(0), + _useState2 = _slicedToArray(_useState, 2), + plotCreated = _useState2[0], + setPlotCreated = _useState2[1]; //0: false, 1: true + + + React.useEffect(function () { + Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('plotly.js-basic-dist')); }).then(function (Plotly) { + //import Plotly dist when Page has been generated + Plot = createPlotlyComponent__default['default'](Plotly); + setPlotCreated(1); + }); + }, []); + + if (!plotCreated) { + return /*#__PURE__*/React__default['default'].createElement("div", null, "Loading..."); + } + + return /*#__PURE__*/React__default['default'].createElement("div", { + "data-testid": "plotlyChart" + }, /*#__PURE__*/React__default['default'].createElement(Plot, _extends({}, spec, { + layout: { + autosize: true + }, + style: { + width: "100%", + height: "100%" + }, + useResizeHandler: true + }))); +}; + +PlotlyChart.propTypes = { + spec: PropTypes__default['default'].object.isRequired +}; + +/** + * KeyInfo component receives two arguments. + * @param {Object} descriptor A Frictionless datapackage descriptor object with the following fields: + * { + * title: "Title of the data package", + * length: "The number of resources present in the data package" + * datasetSize: The combined size of the data package resources + * format: The format of resources in the dataset. e.g csv, json, excel + * created: The date the dataset was created + * updated: The date the dataset was last updated + * licence: The licence of the dataset + * sources: An array of the data set sources + * } + * @param {Array} resources A Frictionless datapackage resource array + * @returns React Component + */ + +var KeyInfo = function KeyInfo(_ref) { + var descriptor = _ref.descriptor, + resources = _ref.resources; + var datasetSize = 0; + + if (resources) { + datasetSize = resources.length == 1 ? resources[0].size : resources.reduce(function (accumulator, currentValue) { + return accumulator.size + currentValue.size; + }); + } + + return /*#__PURE__*/React__default['default'].createElement(React__default['default'].Fragment, null, /*#__PURE__*/React__default['default'].createElement("section", { + className: "m-8", + name: "key-info", + id: "key-info" + }, /*#__PURE__*/React__default['default'].createElement("h1", { + "data-testid": "datasetTitle", + className: "text-3xl font-bold mb-8" + }, descriptor.title), /*#__PURE__*/React__default['default'].createElement("h1", { + className: "text-2xl font-bold mb-4" + }, "Key info"), /*#__PURE__*/React__default['default'].createElement("div", { + className: "grid grid-cols-7 gap-4" + }, /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Files")), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Size")), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Format")), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Created")), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Updated")), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Licence")), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Source"))), /*#__PURE__*/React__default['default'].createElement("div", { + className: "grid grid-cols-7 gap-4" + }, /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl" + }, resources.length)), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl" + }, filesize__default['default'](datasetSize, { + bits: true + }))), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl" + }, resources[0].format, " zip")), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl" + }, descriptor.created)), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl" + }, descriptor.updated)), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl" + }, descriptor.license)), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl" + }, /*#__PURE__*/React__default['default'].createElement("a", { + className: "text-yellow-600", + href: descriptor.sources[0].web + }, descriptor.sources[0].title)))))); +}; + +KeyInfo.propTypes = { + descriptor: PropTypes__default['default'].object.isRequired, + resources: PropTypes__default['default'].array.isRequired +}; + +/** + * ResourceInfo component displays all resources in a data package + * @param {Array} resources A Frictionless datapackage resource object + * @returns React Component + */ + +var ResourcesInfo = function ResourcesInfo(_ref) { + var resources = _ref.resources; + return /*#__PURE__*/React__default['default'].createElement(React__default['default'].Fragment, null, /*#__PURE__*/React__default['default'].createElement("section", { + className: "m-8", + name: "file-list" + }, /*#__PURE__*/React__default['default'].createElement("h1", { + className: "text-2xl font-bold mb-4" + }, "Data Files"), /*#__PURE__*/React__default['default'].createElement("div", { + className: "grid grid-cols-7 gap-4" + }, /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "File")), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Description")), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Size")), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Last Changed")), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Download"))), resources.map(function (resource, index) { + return /*#__PURE__*/React__default['default'].createElement("div", { + key: "".concat(index, "_").concat(resource.name), + className: "grid grid-cols-7 gap-4" + }, /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl" + }, resource.name)), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl" + }, resource.description || "No description")), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl" + }, filesize__default['default'](resource.size, { + bits: true + }))), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl" + }, resource.updated)), /*#__PURE__*/React__default['default'].createElement("div", null, /*#__PURE__*/React__default['default'].createElement("h3", { + className: "text-1xl" + }, /*#__PURE__*/React__default['default'].createElement("a", { + className: "text-yellow-600", + href: "/dataset/".concat(resource.path) + }, resource.format, " (", filesize__default['default'](resource.size, { + bits: true + }), ")")))); + }))); +}; + +ResourcesInfo.propTypes = { + resources: PropTypes__default['default'].array.isRequired +}; + +/** + * ReadMe component displays the markdown description of a datapackage + * @param {string} readme parsed html of data package readme + * @returns React Component + */ + +var ReadMe = function ReadMe(_ref) { + var readmeHtml = _ref.readmeHtml; + return /*#__PURE__*/React__default['default'].createElement(React__default['default'].Fragment, null, /*#__PURE__*/React__default['default'].createElement("section", { + className: "m-8", + name: "sample-table" + }, /*#__PURE__*/React__default['default'].createElement("h1", { + className: "text-2xl font-bold mb-4" + }, "README"), /*#__PURE__*/React__default['default'].createElement("div", { + className: "prose" + }, /*#__PURE__*/React__default['default'].createElement("div", { + dangerouslySetInnerHTML: { + __html: readmeHtml + } + })))); +}; + +ReadMe.propTypes = { + readmeHtml: PropTypes__default['default'].string.isRequired +}; + +/** + * Opens a frictionless resource in data explorer. Data explorer gives you + * an interface to interact with a resource. That means you can do things like + * data filtering, sorting, e.t.c + * @param {object} resource A frictionless Data resource + * @returns React component + */ + +var DataExplorer = function DataExplorer(_ref) { + var resource = _ref.resource; + // TODO: Add data explorer code + return /*#__PURE__*/React__default['default'].createElement(React__default['default'].Fragment, null, JSON.stringify(resource)); +}; + +DataExplorer.propTypes = { + resource: PropTypes__default['default'].object.isRequired +}; + +/** + * Displays information about an organization in a dataset page + * @param {Object} props object describing the dataset organization. + * organization: { + * image_url: The image url of the organization + * name: The name of the organization + * title: The title of the organization + * } + * @returns + */ + +var Org = function Org(_ref) { + var organization = _ref.organization; + return /*#__PURE__*/React__default['default'].createElement(React__default['default'].Fragment, null, organization ? /*#__PURE__*/React__default['default'].createElement(React__default['default'].Fragment, null, /*#__PURE__*/React__default['default'].createElement("img", { + src: organization.image_url || 'https://datahub.io/static/img/datahub-cube-edited.svg', + className: "h-5 w-5 mr-2 inline-block", + alt: "org_img" + }), /*#__PURE__*/React__default['default'].createElement(Link__default['default'], { + href: "/@".concat(organization.name) + }, /*#__PURE__*/React__default['default'].createElement("a", { + className: "font-semibold text-primary underline" + }, organization.title || organization.name))) : ''); +}; + +Org.propTypes = { + organization: PropTypes__default['default'].object.isRequired +}; + +/** + * Displays a blog post page + * @param {object} props + * { + * title: + * content: + * modified: + * excerpt: + * } + * } + * @returns + */ + +var PostList = function PostList(_ref) { + var posts = _ref.posts; + return /*#__PURE__*/React__default['default'].createElement(React__default['default'].Fragment, null, /*#__PURE__*/React__default['default'].createElement("h1", { + className: "text-3xl font-semibold text-primary my-6 inline-block" + }, posts.length, " posts found"), posts.map(function (post, index) { + return /*#__PURE__*/React__default['default'].createElement("div", { + key: index + }, /*#__PURE__*/React__default['default'].createElement("a", { + href: "/blog/".concat(post.slug), + className: "text-2xl font-semibold text-primary my-6 inline-block" + }, parse__default['default'](post.title)), /*#__PURE__*/React__default['default'].createElement("p", null, parse__default['default'](post.excerpt))); + })); +}; + +PostList.propTypes = { + posts: PropTypes__default['default'].object.isRequired +}; + +/** + * Error message component with consistent portal style + * @param {object} props + * { + * message: The error message to display + * } + * @returns + */ + +var ErrorMessage = function ErrorMessage(_ref) { + var message = _ref.message; + return /*#__PURE__*/React__default['default'].createElement("aside", null, message, /*#__PURE__*/React__default['default'].createElement("style", { + jsx: true + }, "\n aside {\n padding: 1.5em;\n font-size: 14px;\n color: white;\n background-color: red;\n }\n ")); +}; + +ErrorMessage.propTypes = { + message: PropTypes__default['default'].string.isRequired +}; + +/** + * Creates a custom link with title + * @param {object} props + * { + * url: The url of the custom link + * title: The title for the custom link + * } + * @returns React Component + */ + +var CustomLink = function CustomLink(_ref) { + var url = _ref.url, + title = _ref.title; + return /*#__PURE__*/React__default['default'].createElement("a", { + href: url, + className: "bg-white hover:bg-gray-200 border text-black font-semibold py-2 px-4 rounded" + }, title); +}; + +CustomLink.propTypes = { + url: PropTypes__default['default'].string.isRequired, + title: PropTypes__default['default'].string.isRequired +}; + +/** + * Displays a navigation bar with logo and menu links + * @param {Object} props object with the following properties: + * { + * logo: The relative url to the logo image + * navMenu: An array of objects with menu items. E.g : [{ title: 'Blog', path: '/blog' },{ title: 'Search', path: '/search' }] + * } + * @returns React Component + */ + +var Nav = function Nav(_ref) { + var logo = _ref.logo, + navMenu = _ref.navMenu; + + var _useState = React.useState(false), + _useState2 = _slicedToArray(_useState, 2), + open = _useState2[0], + setOpen = _useState2[1]; + + var handleClick = function handleClick(event) { + event.preventDefault(); + setOpen(!open); + }; + + return /*#__PURE__*/React__default['default'].createElement("nav", { + className: "flex items-center justify-between flex-wrap bg-white p-4 border-b border-gray-200" + }, /*#__PURE__*/React__default['default'].createElement("div", { + className: "flex items-center flex-shrink-0 text-gray-700 mr-6" + }, /*#__PURE__*/React__default['default'].createElement("img", { + src: logo, + alt: "portal logo", + width: "40" + })), /*#__PURE__*/React__default['default'].createElement("div", { + className: "block lg:hidden mx-4" + }, /*#__PURE__*/React__default['default'].createElement("button", { + onClick: handleClick, + className: "flex items-center px-3 py-2 border rounded text-gray-700 border-orange-400 hover:text-black hover:border-black" + }, /*#__PURE__*/React__default['default'].createElement("svg", { + className: "fill-current h-3 w-3", + viewBox: "0 0 20 20", + xmlns: "http://www.w3.org/2000/svg" + }, /*#__PURE__*/React__default['default'].createElement("title", null, "Menu"), /*#__PURE__*/React__default['default'].createElement("path", { + d: "M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" + })))), /*#__PURE__*/React__default['default'].createElement("div", { + className: "".concat(open ? "block" : "hidden", " lg:block") + }, navMenu.map(function (menu, index) { + return /*#__PURE__*/React__default['default'].createElement(Link__default['default'], { + href: menu.path, + key: index + }, /*#__PURE__*/React__default['default'].createElement("a", { + className: "block mt-4 lg:inline-block lg:mt-0 active:bg-primary-background text-gray-700 hover:text-black mr-6" + }, menu.title)); + }))); +}; + +Nav.propTypes = { + logo: PropTypes__default['default'].string.isRequired, + navMenu: PropTypes__default['default'].string +}; + +/** + * Displays a list of recent datasets + * @param {object} props + * datasets = { + * organization: {name: , title: }, + * title: + * name: + * description: + * notes: + * } + * @returns React Component + */ + +var Recent = function Recent(_ref) { + var datasets = _ref.datasets; + return /*#__PURE__*/React__default['default'].createElement("section", { + className: "my-10 mx-4 lg:my-20" + }, /*#__PURE__*/React__default['default'].createElement("h1", { + className: "text-2xl font-thin mb-4" + }, "Recent Datasets"), /*#__PURE__*/React__default['default'].createElement("div", { + className: "recent flex flex-col lg:flex-row" + }, datasets.map(function (dataset, index) { + return /*#__PURE__*/React__default['default'].createElement("div", { + key: index, + className: "border px-4 mb-4 mr-3 border-gray-100 w-5/6 shadow-sm" + }, /*#__PURE__*/React__default['default'].createElement("h1", { + className: "text-2xl font-thin" + }, dataset.title), /*#__PURE__*/React__default['default'].createElement("p", { + className: "text-gray-500" + }, dataset.organization && dataset.organization.description), /*#__PURE__*/React__default['default'].createElement(Link__default['default'], { + href: "/@".concat(dataset.organization ? dataset.organization.name : 'dataset', "/").concat(dataset.name) + }, /*#__PURE__*/React__default['default'].createElement("a", { + className: "pt-3 flex justify-end text-orange-500" + }, "View Dataset"))); + }))); +}; + +Recent.propTypes = { + datasets: PropTypes__default['default'].object.isRequired +}; + +exports.CustomLink = CustomLink; +exports.DataExplorer = DataExplorer; +exports.Error = ErrorMessage; +exports.KeyInfo = KeyInfo; +exports.Nav = Nav; +exports.Org = Org; +exports.PlotlyChart = PlotlyChart; +exports.Post = Post; +exports.PostList = PostList; +exports.ReadMe = ReadMe; +exports.Recent = Recent; +exports.ResourceInfo = ResourcesInfo; +exports.Table = Table; diff --git a/dist/index.esm.js b/dist/index.esm.js new file mode 100644 index 00000000..82e9e799 --- /dev/null +++ b/dist/index.esm.js @@ -0,0 +1,575 @@ +import React, { useState, useEffect } from 'react'; +import { DataGrid } from '@material-ui/data-grid'; +import PropTypes from 'prop-types'; +import createPlotlyComponent from 'react-plotly.js/factory'; +import filesize from 'filesize'; +import Link from 'next/link'; +import parse from 'html-react-parser'; + +/** + * Displays dataset in tabular form using data grid + * @param columns: An array of column names with properties: e.g [{field: "col1", headerName: "col1"}, {field: "col2", headerName: "col2"}] + * @param data: an array of data objects e.g. [ {col1: 1, col2: 2}, {col1: 5, col2: 7} ] + */ + +var Table = function Table(_ref) { + var columns = _ref.columns, + data = _ref.data; + return /*#__PURE__*/React.createElement("div", { + "data-testid": "tableGrid", + style: { + height: 400, + width: '100%' + } + }, /*#__PURE__*/React.createElement(DataGrid, { + rows: data, + columns: columns, + pageSize: 5 + })); +}; + +Table.propTypes = { + columns: PropTypes.array.isRequired, + data: PropTypes.array.isRequired +}; + +function _extends() { + _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + return _extends.apply(this, arguments); +} + +function _slicedToArray(arr, i) { + return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); +} + +function _arrayWithHoles(arr) { + if (Array.isArray(arr)) return arr; +} + +function _iterableToArrayLimit(arr, i) { + var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); + + if (_i == null) return; + var _arr = []; + var _n = true; + var _d = false; + + var _s, _e; + + try { + for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { + _arr.push(_s.value); + + if (i && _arr.length === i) break; + } + } catch (err) { + _d = true; + _e = err; + } finally { + try { + if (!_n && _i["return"] != null) _i["return"](); + } finally { + if (_d) throw _e; + } + } + + return _arr; +} + +function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); +} + +function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + + return arr2; +} + +function _nonIterableRest() { + throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +} + +var Plot; + +var PlotlyChart = function PlotlyChart(_ref) { + var spec = _ref.spec; + + var _useState = useState(0), + _useState2 = _slicedToArray(_useState, 2), + plotCreated = _useState2[0], + setPlotCreated = _useState2[1]; //0: false, 1: true + + + useEffect(function () { + import('plotly.js-basic-dist').then(function (Plotly) { + //import Plotly dist when Page has been generated + Plot = createPlotlyComponent(Plotly); + setPlotCreated(1); + }); + }, []); + + if (!plotCreated) { + return /*#__PURE__*/React.createElement("div", null, "Loading..."); + } + + return /*#__PURE__*/React.createElement("div", { + "data-testid": "plotlyChart" + }, /*#__PURE__*/React.createElement(Plot, _extends({}, spec, { + layout: { + autosize: true + }, + style: { + width: "100%", + height: "100%" + }, + useResizeHandler: true + }))); +}; + +PlotlyChart.propTypes = { + spec: PropTypes.object.isRequired +}; + +/** + * KeyInfo component receives two arguments. + * @param {Object} descriptor A Frictionless datapackage descriptor object with the following fields: + * { + * title: "Title of the data package", + * length: "The number of resources present in the data package" + * datasetSize: The combined size of the data package resources + * format: The format of resources in the dataset. e.g csv, json, excel + * created: The date the dataset was created + * updated: The date the dataset was last updated + * licence: The licence of the dataset + * sources: An array of the data set sources + * } + * @param {Array} resources A Frictionless datapackage resource array + * @returns React Component + */ + +var KeyInfo = function KeyInfo(_ref) { + var descriptor = _ref.descriptor, + resources = _ref.resources; + var datasetSize = 0; + + if (resources) { + datasetSize = resources.length == 1 ? resources[0].size : resources.reduce(function (accumulator, currentValue) { + return accumulator.size + currentValue.size; + }); + } + + return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("section", { + className: "m-8", + name: "key-info", + id: "key-info" + }, /*#__PURE__*/React.createElement("h1", { + "data-testid": "datasetTitle", + className: "text-3xl font-bold mb-8" + }, descriptor.title), /*#__PURE__*/React.createElement("h1", { + className: "text-2xl font-bold mb-4" + }, "Key info"), /*#__PURE__*/React.createElement("div", { + className: "grid grid-cols-7 gap-4" + }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Files")), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Size")), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Format")), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Created")), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Updated")), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Licence")), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Source"))), /*#__PURE__*/React.createElement("div", { + className: "grid grid-cols-7 gap-4" + }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl" + }, resources.length)), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl" + }, filesize(datasetSize, { + bits: true + }))), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl" + }, resources[0].format, " zip")), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl" + }, descriptor.created)), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl" + }, descriptor.updated)), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl" + }, descriptor.license)), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl" + }, /*#__PURE__*/React.createElement("a", { + className: "text-yellow-600", + href: descriptor.sources[0].web + }, descriptor.sources[0].title)))))); +}; + +KeyInfo.propTypes = { + descriptor: PropTypes.object.isRequired, + resources: PropTypes.array.isRequired +}; + +/** + * ResourceInfo component displays all resources in a data package + * @param {Array} resources A Frictionless datapackage resource object + * @returns React Component + */ + +var ResourcesInfo = function ResourcesInfo(_ref) { + var resources = _ref.resources; + return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("section", { + className: "m-8", + name: "file-list" + }, /*#__PURE__*/React.createElement("h1", { + className: "text-2xl font-bold mb-4" + }, "Data Files"), /*#__PURE__*/React.createElement("div", { + className: "grid grid-cols-7 gap-4" + }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "File")), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Description")), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Size")), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Last Changed")), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl font-bold mb-2" + }, "Download"))), resources.map(function (resource, index) { + return /*#__PURE__*/React.createElement("div", { + key: "".concat(index, "_").concat(resource.name), + className: "grid grid-cols-7 gap-4" + }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl" + }, resource.name)), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl" + }, resource.description || "No description")), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl" + }, filesize(resource.size, { + bits: true + }))), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl" + }, resource.updated)), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", { + className: "text-1xl" + }, /*#__PURE__*/React.createElement("a", { + className: "text-yellow-600", + href: "/dataset/".concat(resource.path) + }, resource.format, " (", filesize(resource.size, { + bits: true + }), ")")))); + }))); +}; + +ResourcesInfo.propTypes = { + resources: PropTypes.array.isRequired +}; + +/** + * ReadMe component displays the markdown description of a datapackage + * @param {string} readme parsed html of data package readme + * @returns React Component + */ + +var ReadMe = function ReadMe(_ref) { + var readmeHtml = _ref.readmeHtml; + return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("section", { + className: "m-8", + name: "sample-table" + }, /*#__PURE__*/React.createElement("h1", { + className: "text-2xl font-bold mb-4" + }, "README"), /*#__PURE__*/React.createElement("div", { + className: "prose" + }, /*#__PURE__*/React.createElement("div", { + dangerouslySetInnerHTML: { + __html: readmeHtml + } + })))); +}; + +ReadMe.propTypes = { + readmeHtml: PropTypes.string.isRequired +}; + +/** + * Opens a frictionless resource in data explorer. Data explorer gives you + * an interface to interact with a resource. That means you can do things like + * data filtering, sorting, e.t.c + * @param {object} resource A frictionless Data resource + * @returns React component + */ + +var DataExplorer = function DataExplorer(_ref) { + var resource = _ref.resource; + // TODO: Add data explorer code + return /*#__PURE__*/React.createElement(React.Fragment, null, JSON.stringify(resource)); +}; + +DataExplorer.propTypes = { + resource: PropTypes.object.isRequired +}; + +/** + * Displays information about an organization in a dataset page + * @param {Object} props object describing the dataset organization. + * organization: { + * image_url: The image url of the organization + * name: The name of the organization + * title: The title of the organization + * } + * @returns + */ + +var Org = function Org(_ref) { + var organization = _ref.organization; + return /*#__PURE__*/React.createElement(React.Fragment, null, organization ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("img", { + src: organization.image_url || 'https://datahub.io/static/img/datahub-cube-edited.svg', + className: "h-5 w-5 mr-2 inline-block", + alt: "org_img" + }), /*#__PURE__*/React.createElement(Link, { + href: "/@".concat(organization.name) + }, /*#__PURE__*/React.createElement("a", { + className: "font-semibold text-primary underline" + }, organization.title || organization.name))) : ''); +}; + +Org.propTypes = { + organization: PropTypes.object.isRequired +}; + +/** + * Displays a blog post page + * @param {object} props + * { + * title: + * content: + * modified: + * excerpt: + * } + * } + * @returns + */ + +var PostList = function PostList(_ref) { + var posts = _ref.posts; + return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("h1", { + className: "text-3xl font-semibold text-primary my-6 inline-block" + }, posts.length, " posts found"), posts.map(function (post, index) { + return /*#__PURE__*/React.createElement("div", { + key: index + }, /*#__PURE__*/React.createElement("a", { + href: "/blog/".concat(post.slug), + className: "text-2xl font-semibold text-primary my-6 inline-block" + }, parse(post.title)), /*#__PURE__*/React.createElement("p", null, parse(post.excerpt))); + })); +}; + +PostList.propTypes = { + posts: PropTypes.object.isRequired +}; + +/** + * Error message component with consistent portal style + * @param {object} props + * { + * message: The error message to display + * } + * @returns + */ + +var ErrorMessage = function ErrorMessage(_ref) { + var message = _ref.message; + return /*#__PURE__*/React.createElement("aside", null, message, /*#__PURE__*/React.createElement("style", { + jsx: true + }, "\n aside {\n padding: 1.5em;\n font-size: 14px;\n color: white;\n background-color: red;\n }\n ")); +}; + +ErrorMessage.propTypes = { + message: PropTypes.string.isRequired +}; + +/** + * Creates a custom link with title + * @param {object} props + * { + * url: The url of the custom link + * title: The title for the custom link + * } + * @returns React Component + */ + +var CustomLink = function CustomLink(_ref) { + var url = _ref.url, + title = _ref.title; + return /*#__PURE__*/React.createElement("a", { + href: url, + className: "bg-white hover:bg-gray-200 border text-black font-semibold py-2 px-4 rounded" + }, title); +}; + +CustomLink.propTypes = { + url: PropTypes.string.isRequired, + title: PropTypes.string.isRequired +}; + +/** + * Displays a navigation bar with logo and menu links + * @param {Object} props object with the following properties: + * { + * logo: The relative url to the logo image + * navMenu: An array of objects with menu items. E.g : [{ title: 'Blog', path: '/blog' },{ title: 'Search', path: '/search' }] + * } + * @returns React Component + */ + +var Nav = function Nav(_ref) { + var logo = _ref.logo, + navMenu = _ref.navMenu; + + var _useState = useState(false), + _useState2 = _slicedToArray(_useState, 2), + open = _useState2[0], + setOpen = _useState2[1]; + + var handleClick = function handleClick(event) { + event.preventDefault(); + setOpen(!open); + }; + + return /*#__PURE__*/React.createElement("nav", { + className: "flex items-center justify-between flex-wrap bg-white p-4 border-b border-gray-200" + }, /*#__PURE__*/React.createElement("div", { + className: "flex items-center flex-shrink-0 text-gray-700 mr-6" + }, /*#__PURE__*/React.createElement("img", { + src: logo, + alt: "portal logo", + width: "40" + })), /*#__PURE__*/React.createElement("div", { + className: "block lg:hidden mx-4" + }, /*#__PURE__*/React.createElement("button", { + onClick: handleClick, + className: "flex items-center px-3 py-2 border rounded text-gray-700 border-orange-400 hover:text-black hover:border-black" + }, /*#__PURE__*/React.createElement("svg", { + className: "fill-current h-3 w-3", + viewBox: "0 0 20 20", + xmlns: "http://www.w3.org/2000/svg" + }, /*#__PURE__*/React.createElement("title", null, "Menu"), /*#__PURE__*/React.createElement("path", { + d: "M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" + })))), /*#__PURE__*/React.createElement("div", { + className: "".concat(open ? "block" : "hidden", " lg:block") + }, navMenu.map(function (menu, index) { + return /*#__PURE__*/React.createElement(Link, { + href: menu.path, + key: index + }, /*#__PURE__*/React.createElement("a", { + className: "block mt-4 lg:inline-block lg:mt-0 active:bg-primary-background text-gray-700 hover:text-black mr-6" + }, menu.title)); + }))); +}; + +Nav.propTypes = { + logo: PropTypes.string.isRequired, + navMenu: PropTypes.string +}; + +/** + * Displays a list of recent datasets + * @param {object} props + * datasets = { + * organization: {name: , title: }, + * title: + * name: + * description: + * notes: + * } + * @returns React Component + */ + +var Recent = function Recent(_ref) { + var datasets = _ref.datasets; + return /*#__PURE__*/React.createElement("section", { + className: "my-10 mx-4 lg:my-20" + }, /*#__PURE__*/React.createElement("h1", { + className: "text-2xl font-thin mb-4" + }, "Recent Datasets"), /*#__PURE__*/React.createElement("div", { + className: "recent flex flex-col lg:flex-row" + }, datasets.map(function (dataset, index) { + return /*#__PURE__*/React.createElement("div", { + key: index, + className: "border px-4 mb-4 mr-3 border-gray-100 w-5/6 shadow-sm" + }, /*#__PURE__*/React.createElement("h1", { + className: "text-2xl font-thin" + }, dataset.title), /*#__PURE__*/React.createElement("p", { + className: "text-gray-500" + }, dataset.organization && dataset.organization.description), /*#__PURE__*/React.createElement(Link, { + href: "/@".concat(dataset.organization ? dataset.organization.name : 'dataset', "/").concat(dataset.name) + }, /*#__PURE__*/React.createElement("a", { + className: "pt-3 flex justify-end text-orange-500" + }, "View Dataset"))); + }))); +}; + +Recent.propTypes = { + datasets: PropTypes.object.isRequired +}; + +export { CustomLink, DataExplorer, ErrorMessage as Error, KeyInfo, Nav, Org, PlotlyChart, Post, PostList, ReadMe, Recent, ResourcesInfo as ResourceInfo, Table };