From d466023926bf90be6c8a2e3419576849ef861de6 Mon Sep 17 00:00:00 2001 From: Luccas Mateus de Medeiros Gomes Date: Wed, 12 Apr 2023 09:13:17 -0300 Subject: [PATCH] [monorepo][lg] - move over examples --- examples/ckan/.env | 1 + .../{data-literate => ckan}/.eslintrc.json | 5 +- examples/ckan/README.md | 306 + .../__tests__/components/search/Form.test.tsx | 23 + .../search/__snapshots__/Form.test.tsx.snap | 47 + .../ckan/components/_shared/CustomLink.tsx | 15 + examples/ckan/components/_shared/Error.tsx | 17 + examples/ckan/components/_shared/Table.tsx | 53 + examples/ckan/components/_shared/index.ts | 5 + examples/ckan/components/dataset/About.tsx | 83 + examples/ckan/components/dataset/Org.tsx | 23 + .../ckan/components/dataset/Resources.tsx | 76 + examples/ckan/components/home/Footer.tsx | 61 + examples/ckan/components/home/Hero.tsx | 7 + .../ckan/components/home/HeroTemplate.tsx | 130 + examples/ckan/components/home/Nav.tsx | 14 + examples/ckan/components/home/NavTemplate.tsx | 117 + examples/ckan/components/home/Recent.tsx | 96 + examples/ckan/components/home/Stats.tsx | 54 + examples/ckan/components/org/About.tsx | 154 + examples/ckan/components/resource/About.tsx | 78 + examples/ckan/components/resource/Preview.tsx | 32 + examples/ckan/components/resource/View.tsx | 24 + examples/ckan/components/search/Form.tsx | 42 + examples/ckan/components/search/List.tsx | 64 + examples/ckan/components/search/Total.tsx | 23 + examples/ckan/components/static/List.tsx | 39 + examples/ckan/components/static/Page.tsx | 32 + examples/ckan/components/static/Post.tsx | 32 + examples/ckan/config/jest/cssTransform.js | 8 + examples/ckan/cypress.json | 3 + examples/ckan/cypress/fixtures/example.json | 5 + .../integration/pages/homepage-spec.js | 32 + .../cypress/integration/pages/search-spec.js | 21 + examples/ckan/cypress/plugins/index.js | 21 + examples/ckan/cypress/support/commands.js | 25 + examples/ckan/cypress/support/index.js | 20 + examples/ckan/cypress/tsconfig.json | 12 + examples/ckan/graphql/queries.ts | 166 + examples/{data-literate => ckan}/index.d.ts | 0 examples/ckan/jest.config.js | 29 + .../{data-literate => ckan}/jest.config.ts | 4 +- examples/ckan/lib/apolloClient.ts | 93 + examples/ckan/locales/en/common.json | 4 + examples/ckan/locales/fr/common.json | 4 + examples/ckan/mocks/index.js | 155 + .../{data-literate => ckan}/next-env.d.ts | 0 examples/ckan/next.config.js | 65 + examples/ckan/pages/[org]/[dataset]/index.tsx | 53 + .../[org]/[dataset]/r/[resource]/index.tsx | 56 + examples/ckan/pages/[org]/index.tsx | 49 + examples/ckan/pages/_app.tsx | 57 + examples/ckan/pages/_document.tsx | 34 + examples/ckan/pages/blog/[post]/index.tsx | 45 + examples/ckan/pages/blog/index.tsx | 35 + examples/ckan/pages/index.module.css | 2 + examples/ckan/pages/index.tsx | 59 + examples/ckan/pages/p/[page]/index.tsx | 45 + examples/ckan/pages/search.tsx | 49 + examples/ckan/pages/styles.css | 403 + .../{data-literate => ckan}/postcss.config.js | 0 examples/{data-literate => ckan}/project.json | 22 +- .../{data-literate => ckan}/public/.gitkeep | 0 examples/ckan/public/favicon.ico | Bin 0 -> 318 bytes examples/ckan/public/images/banner.svg | 1 + examples/ckan/public/images/logo.svg | 1 + .../specs/index.spec.tsx | 0 examples/ckan/styles/globals.css | 3 + .../tailwind.config.js | 2 +- examples/ckan/themes/base.ts | 13 + examples/ckan/themes/index.ts | 11 + examples/ckan/themes/primary.ts | 6 + examples/ckan/themes/utils.ts | 47 + .../{data-literate => ckan}/tsconfig.json | 0 .../tsconfig.spec.json | 0 examples/ckan/utils/index.js | 541 + examples/data-literate/README.md | 60 - .../data-literate/components/DataLiterate.js | 45 - examples/data-literate/components/Excel.js | 74 - examples/data-literate/components/Layout.js | 29 - .../data-literate/components/LineChart.js | 33 - examples/data-literate/components/Table.js | 83 - .../data-literate/components/TableGrid.js | 81 - examples/data-literate/content/demo.mdx | 331 - .../data-literate/datahub-portal-local-cli.js | 18 - examples/data-literate/lib/markdown.js | 33 - examples/data-literate/lib/mdxUtils.js | 23 - examples/data-literate/next.config.js | 17 - examples/data-literate/pages/[...slug].js | 48 - examples/data-literate/pages/api/proxy.js | 26 - ....analysis.summary_series.global.annual.csv | 173 - ...e-suprasecular-decline-1311-2018-data.xlsx | Bin 2170589 -> 0 bytes .../data-literate/public/datopian-logo.png | Bin 33976 -> 0 bytes examples/data-literate/public/favicon.ico | Bin 15406 -> 0 bytes examples/data-literate/styles/globals.css | 16 - examples/dataset-frictionless/.babelrc | 5 + examples/dataset-frictionless/.gitignore | 30 + examples/dataset-frictionless/README.md | 39 + examples/dataset-frictionless/assets/demo.gif | Bin 0 -> 9041182 bytes examples/dataset-frictionless/lib/dataset.js | 70 + examples/dataset-frictionless/lib/utils.js | 110 + examples/dataset-frictionless/package.json | 38 + .../pages/_app.js | 0 examples/dataset-frictionless/pages/index.js | 109 + .../dataset-frictionless/postcss.config.js | 8 + .../public/country-codes/README.md | 77 + .../country-codes/archive/country-codes.csv | 251 + .../country-codes/data/country-codes_csv.csv | 251 + .../public/country-codes/datapackage.json | 908 + .../public/dataset/README.md | 57 + .../public/dataset/data/vix-daily.csv | 3726 ++++ .../public/dataset/datapackage.json | 84 + .../dataset-frictionless/public/favicon.ico | Bin 0 -> 15086 bytes .../dataset-frictionless/styles/globals.css | 3 + .../styles/tailwind.css | 0 .../dataset-frictionless/tailwind.config.js | 13 + examples/dataset-frictionless/yarn.lock | 14100 ++++++++++++++++ nx.json | 3 +- package-lock.json | 6331 +------ package.json | 13 +- .../src/components/views/Table.js | 2 +- tsconfig.base.json | 4 +- 122 files changed, 24606 insertions(+), 6570 deletions(-) create mode 100644 examples/ckan/.env rename examples/{data-literate => ckan}/.eslintrc.json (81%) create mode 100644 examples/ckan/README.md create mode 100644 examples/ckan/__tests__/components/search/Form.test.tsx create mode 100644 examples/ckan/__tests__/components/search/__snapshots__/Form.test.tsx.snap create mode 100644 examples/ckan/components/_shared/CustomLink.tsx create mode 100644 examples/ckan/components/_shared/Error.tsx create mode 100644 examples/ckan/components/_shared/Table.tsx create mode 100644 examples/ckan/components/_shared/index.ts create mode 100644 examples/ckan/components/dataset/About.tsx create mode 100644 examples/ckan/components/dataset/Org.tsx create mode 100644 examples/ckan/components/dataset/Resources.tsx create mode 100644 examples/ckan/components/home/Footer.tsx create mode 100644 examples/ckan/components/home/Hero.tsx create mode 100644 examples/ckan/components/home/HeroTemplate.tsx create mode 100644 examples/ckan/components/home/Nav.tsx create mode 100644 examples/ckan/components/home/NavTemplate.tsx create mode 100644 examples/ckan/components/home/Recent.tsx create mode 100644 examples/ckan/components/home/Stats.tsx create mode 100644 examples/ckan/components/org/About.tsx create mode 100644 examples/ckan/components/resource/About.tsx create mode 100644 examples/ckan/components/resource/Preview.tsx create mode 100644 examples/ckan/components/resource/View.tsx create mode 100644 examples/ckan/components/search/Form.tsx create mode 100644 examples/ckan/components/search/List.tsx create mode 100644 examples/ckan/components/search/Total.tsx create mode 100644 examples/ckan/components/static/List.tsx create mode 100644 examples/ckan/components/static/Page.tsx create mode 100644 examples/ckan/components/static/Post.tsx create mode 100644 examples/ckan/config/jest/cssTransform.js create mode 100644 examples/ckan/cypress.json create mode 100644 examples/ckan/cypress/fixtures/example.json create mode 100644 examples/ckan/cypress/integration/pages/homepage-spec.js create mode 100644 examples/ckan/cypress/integration/pages/search-spec.js create mode 100644 examples/ckan/cypress/plugins/index.js create mode 100644 examples/ckan/cypress/support/commands.js create mode 100644 examples/ckan/cypress/support/index.js create mode 100644 examples/ckan/cypress/tsconfig.json create mode 100644 examples/ckan/graphql/queries.ts rename examples/{data-literate => ckan}/index.d.ts (100%) create mode 100644 examples/ckan/jest.config.js rename examples/{data-literate => ckan}/jest.config.ts (75%) create mode 100644 examples/ckan/lib/apolloClient.ts create mode 100644 examples/ckan/locales/en/common.json create mode 100644 examples/ckan/locales/fr/common.json create mode 100644 examples/ckan/mocks/index.js rename examples/{data-literate => ckan}/next-env.d.ts (100%) create mode 100644 examples/ckan/next.config.js create mode 100644 examples/ckan/pages/[org]/[dataset]/index.tsx create mode 100644 examples/ckan/pages/[org]/[dataset]/r/[resource]/index.tsx create mode 100644 examples/ckan/pages/[org]/index.tsx create mode 100644 examples/ckan/pages/_app.tsx create mode 100644 examples/ckan/pages/_document.tsx create mode 100644 examples/ckan/pages/blog/[post]/index.tsx create mode 100644 examples/ckan/pages/blog/index.tsx create mode 100644 examples/ckan/pages/index.module.css create mode 100644 examples/ckan/pages/index.tsx create mode 100644 examples/ckan/pages/p/[page]/index.tsx create mode 100644 examples/ckan/pages/search.tsx create mode 100644 examples/ckan/pages/styles.css rename examples/{data-literate => ckan}/postcss.config.js (100%) rename examples/{data-literate => ckan}/project.json (68%) rename examples/{data-literate => ckan}/public/.gitkeep (100%) create mode 100644 examples/ckan/public/favicon.ico create mode 100644 examples/ckan/public/images/banner.svg create mode 100644 examples/ckan/public/images/logo.svg rename examples/{data-literate => ckan}/specs/index.spec.tsx (100%) create mode 100644 examples/ckan/styles/globals.css rename examples/{data-literate => ckan}/tailwind.config.js (97%) create mode 100644 examples/ckan/themes/base.ts create mode 100644 examples/ckan/themes/index.ts create mode 100644 examples/ckan/themes/primary.ts create mode 100644 examples/ckan/themes/utils.ts rename examples/{data-literate => ckan}/tsconfig.json (100%) rename examples/{data-literate => ckan}/tsconfig.spec.json (100%) create mode 100644 examples/ckan/utils/index.js delete mode 100644 examples/data-literate/README.md delete mode 100644 examples/data-literate/components/DataLiterate.js delete mode 100644 examples/data-literate/components/Excel.js delete mode 100644 examples/data-literate/components/Layout.js delete mode 100644 examples/data-literate/components/LineChart.js delete mode 100644 examples/data-literate/components/Table.js delete mode 100644 examples/data-literate/components/TableGrid.js delete mode 100644 examples/data-literate/content/demo.mdx delete mode 100644 examples/data-literate/datahub-portal-local-cli.js delete mode 100644 examples/data-literate/lib/markdown.js delete mode 100644 examples/data-literate/lib/mdxUtils.js delete mode 100644 examples/data-literate/next.config.js delete mode 100644 examples/data-literate/pages/[...slug].js delete mode 100644 examples/data-literate/pages/api/proxy.js delete mode 100644 examples/data-literate/public/_files/HadCRUT.5.0.1.0.analysis.summary_series.global.annual.csv delete mode 100644 examples/data-literate/public/_files/eight-centuries-of-global-real-interest-rates-r-g-and-the-suprasecular-decline-1311-2018-data.xlsx delete mode 100644 examples/data-literate/public/datopian-logo.png delete mode 100644 examples/data-literate/public/favicon.ico delete mode 100644 examples/data-literate/styles/globals.css create mode 100644 examples/dataset-frictionless/.babelrc create mode 100644 examples/dataset-frictionless/.gitignore create mode 100644 examples/dataset-frictionless/README.md create mode 100644 examples/dataset-frictionless/assets/demo.gif create mode 100644 examples/dataset-frictionless/lib/dataset.js create mode 100644 examples/dataset-frictionless/lib/utils.js create mode 100644 examples/dataset-frictionless/package.json rename examples/{data-literate => dataset-frictionless}/pages/_app.js (100%) create mode 100644 examples/dataset-frictionless/pages/index.js create mode 100644 examples/dataset-frictionless/postcss.config.js create mode 100644 examples/dataset-frictionless/public/country-codes/README.md create mode 100644 examples/dataset-frictionless/public/country-codes/archive/country-codes.csv create mode 100644 examples/dataset-frictionless/public/country-codes/data/country-codes_csv.csv create mode 100644 examples/dataset-frictionless/public/country-codes/datapackage.json create mode 100644 examples/dataset-frictionless/public/dataset/README.md create mode 100644 examples/dataset-frictionless/public/dataset/data/vix-daily.csv create mode 100644 examples/dataset-frictionless/public/dataset/datapackage.json create mode 100644 examples/dataset-frictionless/public/favicon.ico create mode 100644 examples/dataset-frictionless/styles/globals.css rename examples/{data-literate => dataset-frictionless}/styles/tailwind.css (100%) create mode 100644 examples/dataset-frictionless/tailwind.config.js create mode 100644 examples/dataset-frictionless/yarn.lock diff --git a/examples/ckan/.env b/examples/ckan/.env new file mode 100644 index 00000000..262c6bbd --- /dev/null +++ b/examples/ckan/.env @@ -0,0 +1 @@ +DMS=https://demo.dev.datopian.com/ diff --git a/examples/data-literate/.eslintrc.json b/examples/ckan/.eslintrc.json similarity index 81% rename from examples/data-literate/.eslintrc.json rename to examples/ckan/.eslintrc.json index 82b9aa29..51f18cc1 100644 --- a/examples/data-literate/.eslintrc.json +++ b/examples/ckan/.eslintrc.json @@ -10,10 +10,7 @@ { "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], "rules": { - "@next/next/no-html-link-for-pages": [ - "error", - "apps/data-literate/pages" - ] + "@next/next/no-html-link-for-pages": ["error", "packages/ckan/pages"] } }, { diff --git a/examples/ckan/README.md b/examples/ckan/README.md new file mode 100644 index 00000000..74592e54 --- /dev/null +++ b/examples/ckan/README.md @@ -0,0 +1,306 @@ +

+ +🌀 Portal.JS
+The javascript framework for
+data portals + +

+ +🌀 `Portal` is a framework for rapidly building rich data portal frontends using a modern frontend approach (javascript, React, SSR). + +`Portal` 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][]. `portal` is built in Javascript and React on top of the popular [Next.js][] framework. + +[ckan]: https://ckan.org/ +[next.js]: https://nextjs.com/ + +Live DEMO: https://catalog-portal-js.vercel.app + +## Features + +- 🗺️ Unified sites: present data and content in one seamless site, pulling datasets from a DMS (e.g. CKAN) and content from a CMS (e.g. wordpress) with a common internal API. +- 👩‍💻 Developer friendly: built with familiar frontend tech Javascript, React etc +- 🔋 Batteries included: Full set of portal components out of the box e.g. catalog search, dataset showcase, blog etc. +- 🎨 Easy to theme and customize: installable themes, use standard CSS and React+CSS tooling. Add new routes quickly. +- 🧱 Extensible: quickly extend and develop/import your own React components +- 📝 Well documented: full set of documentation plus the documentation of NextJS and Apollo. + +### For developers + +- 🏗 Build with modern, familiar frontend tech such as Javascript and React. +- 🚀 NextJS framework: so everything in NextJS for free React, SSR, static site generation, huge number of examples and integrations etc. + - SSR => unlimited number of pages, SEO etc whilst still using React. + - Static Site Generation (SSG) (good for small sites) => ultra-simple deployment, great performance and lighthouse scores etc +- 📋 Typescript support + +## Getting Started + +### Setup + +Install a recent version of Node. You'll need Node 10.13 or later. + +### Create a Portal app + +To create a Portal app, open your terminal, cd into the directory you'd like to create the app in, and run the following command: + +```console +npm init portal-app my-data-portal +``` + +> NB: Under the hood, this uses the tool called create-next-app, which bootstraps a Next.js app for you. It uses this template through the --example flag. +> +> If it doesn’t work, please open an issue. + +## Guide + +### Styling 🎨 + +We use Tailwind as a CSS framework. Take a look at `/styles/index.css` to see what we're importing from Tailwind bundle. You can also configure Tailwind using `tailwind.config.js` file. + +Have a look at Next.js support of CSS and ways of writing CSS: + +https://nextjs.org/docs/basic-features/built-in-css-support + +### Backend + +So far the app is running with mocked data behind. You can connect CMS and DMS backends easily via environment variables: + +```console +$ export DMS=http://ckan:5000 +$ export CMS=http://myblog.wordpress.com +``` + +> Note that we don't yet have implementations for the following CKAN features: +> +> - Activities +> - Auth +> - Groups +> - Facets + +### Routes + +These are the default routes set up in the "starter" app. + +- Home `/` +- Search `/search` +- Dataset `/@org/dataset` +- Resource `/@org/dataset/r/resource` +- Organization `/@org` +- Collection (aka group in CKAN) (?) - suggest to merge into org +- Static pages, eg, `/about` etc. from CMS or can do it without external CMS, e.g., in Next.js + +### New Routes + +TODO + +### Data fetching + +We use Apollo client which allows us to query data with GraphQL. We have setup CKAN API for the demo (it uses demo.ckan.org as DMS): + +http://portal.datopian1.now.sh/ + +Note that we don't have Apollo Server but we connect CKAN API using [`apollo-link-rest`](https://www.apollographql.com/docs/link/links/rest/) module. You can see how it works in [lib/apolloClient.ts](https://github.com/datopian/portal/blob/master/lib/apolloClient.ts) and then have a look at [pages/\_app.tsx](https://github.com/datopian/portal/blob/master/pages/_app.tsx). + +For development/debugging purposes, we suggest installing the Chrome extension - https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm. + +#### i18n configuration + +Portal.js is configured by default to support both `English` and `French` subpath for language translation. But for subsequent users, this following steps can be used to configure i18n for other languages; + +1. Update `next.config.js`, to add more languages to the i18n locales + +```js +i18n: { + locales: ['en', 'fr', 'nl-NL'], // add more language to the list + defaultLocale: 'en', // set the default language to use +}, +``` + +2. Create a folder for the language in `locales` --> `locales/en-Us` + +3. In the language folder, different namespace files (json) can be created for each translation. For the `index.js` use-case, I named it `common.json` + +```json +// locales/en/common.json +{ + "title" : "Portal js in English", +} + +// locales/fr/common.json +{ + "title" : "Portal js in French", +} +``` + +4. To use on pages using Server-side Props. + +```js +import { loadNamespaces } from './_app'; +import useTranslation from 'next-translate/useTranslation'; + +const Home: React.FC = ()=> { + const { t } = useTranslation(); + return ( +
{t(`common:title`)}
// we use common and title base on the common.json data + ); +}; + +export const getServerSideProps: GetServerSideProps = async ({ locale }) => { + ........ ........ + return { + props : { + _ns: await loadNamespaces(['common'], locale), + } + }; +}; + +``` + +5. Go to the browser and view the changes using language subpath like this `http://localhost:3000` and `http://localhost:3000/fr`. **Note** The subpath also activate chrome language Translator + +#### Pre-fetch data in the server-side + +When visiting a dataset page, you may want to fetch the dataset metadata in the server-side. To do so, you can use `getServerSideProps` function from NextJS: + +```javascript +import { GetServerSideProps } from 'next'; +import { initializeApollo } from '../lib/apolloClient'; +import gql from 'graphql-tag'; + +const QUERY = gql` + query dataset($id: String) { + dataset(id: $id) @rest(type: "Response", path: "package_show?{args}") { + result + } + } +`; + +... + +export const getServerSideProps: GetServerSideProps = async (context) => { + const apolloClient = initializeApollo(); + + await apolloClient.query({ + query: QUERY, + variables: { + id: 'my-dataset' + }, + }); + + return { + props: { + initialApolloState: apolloClient.cache.extract(), + }, + }; +}; +``` + +This would fetch the data from DMS and save it in the Apollo cache so that we can query it again from the components. + +#### Access data from a component + +Consider situation when rendering a component for org info on the dataset page. We already have pre-fetched dataset metadata that includes `organization` property with attributes such as `name`, `title` etc. We can now query only organization part for our `Org` component: + +```javascript +import { useQuery } from '@apollo/react-hooks'; +import gql from 'graphql-tag'; + +export const GET_ORG_QUERY = gql` + query dataset($id: String) { + dataset(id: $id) @rest(type: "Response", path: "package_show?{args}") { + result { + organization { + name + title + image_url + } + } + } + } +`; + +export default function Org({ variables }) { + const { loading, error, data } = useQuery( + GET_ORG_QUERY, + { + variables: { id: 'my-dataset' } + } + ); + + ... + + const { organization } = data.dataset.result; + + return ( + <> + {organization ? ( + <> + + + + {organization.title || organization.name} + + + + ) : ( + '' + )} + + ); +} +``` + +#### Add a new data source + +TODO + +## Developers + +### Boot the local instance + +Install the dependencies: + +```bash +yarn # or npm i +``` + +Boot the demo portal: + +```console +$ yarn dev # or npm run dev +``` + +Open [http://localhost:3000](http://localhost:3000) to see the home page 🎉 + +You can start editing the page by modifying `/pages/index.tsx`. The page auto-updates as you edit the file. + +### Tests + +We use Jest for running tests: + +```bash +yarn test # or npm run test + +# turn on watching +yarn test --watch +``` + +We use Cypress tests as well + +``` +yarn run e2e +``` + +### Architecture + +- Language: Javascript +- Framework: NextJS - https://nextjs.org/ +- Data layer API: GraphQL using Apollo. So controllers access data using GraphQL “gatsby like” + +### Key Pages + +See https://tech.datopian.com/frontend/ diff --git a/examples/ckan/__tests__/components/search/Form.test.tsx b/examples/ckan/__tests__/components/search/Form.test.tsx new file mode 100644 index 00000000..24b0160d --- /dev/null +++ b/examples/ckan/__tests__/components/search/Form.test.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import Form from '../../../components/search/Form'; + +const useRouter = jest.spyOn(require('next/router'), 'useRouter'); + +test('📸 of Form component with empty', () => { + useRouter.mockImplementationOnce(() => ({ + query: { search: '', sort: '' }, + })); + + const { container } = render(
); + expect(container).toMatchSnapshot(); +}); + +test('📸 of Form component with query', () => { + useRouter.mockImplementationOnce(() => ({ + query: { search: 'gdp', sort: '' }, + })); + + const { container } = render(); + expect(container).toMatchSnapshot(); +}); diff --git a/examples/ckan/__tests__/components/search/__snapshots__/Form.test.tsx.snap b/examples/ckan/__tests__/components/search/__snapshots__/Form.test.tsx.snap new file mode 100644 index 00000000..d8dfe846 --- /dev/null +++ b/examples/ckan/__tests__/components/search/__snapshots__/Form.test.tsx.snap @@ -0,0 +1,47 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`📸 of Form component with empty 1`] = ` +
+ + + + +
+`; + +exports[`📸 of Form component with query 1`] = ` +
+
+ + +
+
+`; diff --git a/examples/ckan/components/_shared/CustomLink.tsx b/examples/ckan/components/_shared/CustomLink.tsx new file mode 100644 index 00000000..2b50236e --- /dev/null +++ b/examples/ckan/components/_shared/CustomLink.tsx @@ -0,0 +1,15 @@ +type LinkProps = { + url: string; + format: any; +}; + +const CustomLink: React.FC = ({ url, format }: LinkProps) => ( + + {format} + +); + +export default CustomLink; diff --git a/examples/ckan/components/_shared/Error.tsx b/examples/ckan/components/_shared/Error.tsx new file mode 100644 index 00000000..afdb4cec --- /dev/null +++ b/examples/ckan/components/_shared/Error.tsx @@ -0,0 +1,17 @@ +const ErrorMessage: React.FC<{ message: any }> = ({ message }) => { + return ( + + ); +}; + +export default ErrorMessage; diff --git a/examples/ckan/components/_shared/Table.tsx b/examples/ckan/components/_shared/Table.tsx new file mode 100644 index 00000000..deccbf49 --- /dev/null +++ b/examples/ckan/components/_shared/Table.tsx @@ -0,0 +1,53 @@ +interface TableProps { + columns: Array; + data: Array; + className?: string; +} + +const Table: React.FC = ({ columns, data, className }) => { + return ( +
+
+
+ + + + {columns.map(({ key, name }) => ( + + ))} + + + + {data.map((item) => ( + + {columns.map(({ key, render }) => ( + + ))} + + ))} + +
+ {name} +
+ {(render && + typeof render === 'function' && + render(item)) || + item[key] || + ''} +
+
+
+
+ ); +}; + +export default Table; diff --git a/examples/ckan/components/_shared/index.ts b/examples/ckan/components/_shared/index.ts new file mode 100644 index 00000000..f0643faa --- /dev/null +++ b/examples/ckan/components/_shared/index.ts @@ -0,0 +1,5 @@ +import Table from './Table'; +import ErrorMessage from './Error'; +import CustomLink from './CustomLink'; + +export { Table, ErrorMessage, CustomLink }; diff --git a/examples/ckan/components/dataset/About.tsx b/examples/ckan/components/dataset/About.tsx new file mode 100644 index 00000000..ea6910a7 --- /dev/null +++ b/examples/ckan/components/dataset/About.tsx @@ -0,0 +1,83 @@ +import { useQuery } from '@apollo/react-hooks'; +import * as timeago from 'timeago.js'; +import { ErrorMessage } from '../_shared'; +import { GET_DATASET_QUERY } from '../../graphql/queries'; + +const About: React.FC<{ variables: any }> = ({ variables }) => { + const { loading, error, data } = useQuery(GET_DATASET_QUERY, { + variables, + // Setting this value to true will make the component rerender when + // the "networkStatus" changes, so we are able to know if it is fetching + // more data + notifyOnNetworkStatusChange: true, + }); + + if (error) return ; + if (loading) return
Loading
; + + const { result } = data.dataset; + + const stats = [ + { name: 'Files', stat: result.resources.length }, + { name: 'Size', stat: result.size || 'N/A' }, + { + name: 'Formats', + stat: result.resources.map((item) => item.format).join(', '), + }, + { + name: 'Created', + stat: result.created && timeago.format(result.created), + }, + { + name: 'Updated', + stat: result.updated && timeago.format(result.updated), + }, + { + name: 'Licenses', + stat: result.licenses?.length + ? result.licenses.map((item, index) => ( + + {item.name} + + )) + : 'N/A', + }, + ]; + + return ( + <> +
+

+ {result.title || result.name} +

+

+ {result.description || 'This dataset does not have a description.'} +

+
+
+
+ {stats.map((item) => ( +
+
+ {item.name} +
+
+ {item.stat} +
+
+ ))} +
+
+ + ); +}; + +export default About; diff --git a/examples/ckan/components/dataset/Org.tsx b/examples/ckan/components/dataset/Org.tsx new file mode 100644 index 00000000..09272c36 --- /dev/null +++ b/examples/ckan/components/dataset/Org.tsx @@ -0,0 +1,23 @@ +import { useQuery } from '@apollo/react-hooks'; +import { ErrorMessage } from '../_shared'; +import { GET_DATASET_QUERY } from '../../graphql/queries'; +import { Org } from '@portaljs/portaljs-components'; + +const OrgInfo: React.FC<{ variables: any }> = ({ variables }) => { + const { loading, error, data } = useQuery(GET_DATASET_QUERY, { + variables, + // Setting this value to true will make the component rerender when + // the "networkStatus" changes, so we are able to know if it is fetching + // more data + notifyOnNetworkStatusChange: true, + }); + + if (error) return ; + if (loading) return
Loading
; + + const { organization } = data.dataset.result; + + return ; +}; + +export default OrgInfo; diff --git a/examples/ckan/components/dataset/Resources.tsx b/examples/ckan/components/dataset/Resources.tsx new file mode 100644 index 00000000..f2702f60 --- /dev/null +++ b/examples/ckan/components/dataset/Resources.tsx @@ -0,0 +1,76 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +/* eslint-disable react/display-name */ +import Link from 'next/link'; +import { useQuery } from '@apollo/react-hooks'; +import * as timeago from 'timeago.js'; +import { Table, ErrorMessage } from '../_shared'; +import { GET_DATASET_QUERY } from '../../graphql/queries'; + +const columns = [ + { + name: 'File', + key: 'file', + render: ({ name: resName, title, parentName }) => ( + + {title || resName} + + ), + }, + { + name: 'Format', + key: 'format', + }, + { + name: 'Created', + key: 'created', + render: ({ created }) => timeago.format(created), + }, + { + name: 'Updated', + key: 'updated', + render: ({ updated }) => timeago.format(updated), + }, + { + name: 'Link', + key: 'link', + render: ({ name: resName, parentName }) => ( + + Preview + + ), + }, +]; + +const Resources: React.FC<{ variables: any }> = ({ variables }) => { + const { loading, error, data } = useQuery(GET_DATASET_QUERY, { + variables, + // Setting this value to true will make the component rerender when + // the "networkStatus" changes, so we are able to know if it is fetching + // more data + notifyOnNetworkStatusChange: true, + }); + + if (error) return ; + if (loading) return
Loading
; + + const { result } = data.dataset; + + return ( +
+
+

+ Data files +

+
+ ({ + ...resource, + parentName: result.name, + }))} + /> + + ); +}; + +export default Resources; diff --git a/examples/ckan/components/home/Footer.tsx b/examples/ckan/components/home/Footer.tsx new file mode 100644 index 00000000..4f871814 --- /dev/null +++ b/examples/ckan/components/home/Footer.tsx @@ -0,0 +1,61 @@ +const Footer: React.FC = () => { + const navigation = { + main: [ + { name: 'Blog', href: '/blog' }, + { name: 'Search', href: '/search' }, + { name: 'Docs', href: '/docs' }, + ], + social: [ + { + name: 'GitHub', + href: 'https://github.com/datopian/portaljs', + // eslint-disable-next-line + icon: (props) => ( + + + + ), + }, + ], + }; + + return ( + + ); +}; + +export default Footer; diff --git a/examples/ckan/components/home/Hero.tsx b/examples/ckan/components/home/Hero.tsx new file mode 100644 index 00000000..07479ae0 --- /dev/null +++ b/examples/ckan/components/home/Hero.tsx @@ -0,0 +1,7 @@ +import Template from './HeroTemplate'; + +const Hero: React.FC = () => { + return