Compare commits
13 Commits
985-tutori
...
1005-note-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
573f938dfe | ||
|
|
961a219f61 | ||
|
|
df395e2b70 | ||
|
|
ea5dade346 | ||
|
|
8027026399 | ||
|
|
af7812f689 | ||
|
|
6a36e65b27 | ||
|
|
38aa62fcef | ||
|
|
ed9b575b4e | ||
|
|
8327f4efc0 | ||
|
|
d6a12e3111 | ||
|
|
9fc834c16d | ||
|
|
1a7371f9c5 |
5
.changeset/thick-worms-peel.md
Normal file
5
.changeset/thick-worms-peel.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@portaljs/remark-wiki-link': minor
|
||||
---
|
||||
|
||||
Parse note embeds as regular wiki links (until we add proper support for note embeds).
|
||||
21
nx.json
21
nx.json
@@ -5,7 +5,13 @@
|
||||
"default": {
|
||||
"runner": "nx/tasks-runners/default",
|
||||
"options": {
|
||||
"cacheableOperations": ["build", "lint", "test", "e2e"]
|
||||
"cacheableOperations": [
|
||||
"build",
|
||||
"lint",
|
||||
"test",
|
||||
"e2e",
|
||||
"build-storybook"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -30,6 +36,14 @@
|
||||
"{workspaceRoot}/.eslintrc.json",
|
||||
"{workspaceRoot}/.eslintignore"
|
||||
]
|
||||
},
|
||||
"build-storybook": {
|
||||
"inputs": [
|
||||
"default",
|
||||
"^production",
|
||||
"{projectRoot}/.storybook/**/*",
|
||||
"{projectRoot}/tsconfig.storybook.json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"namedInputs": {
|
||||
@@ -39,7 +53,10 @@
|
||||
"!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
|
||||
"!{projectRoot}/tsconfig.spec.json",
|
||||
"!{projectRoot}/jest.config.[jt]s",
|
||||
"!{projectRoot}/.eslintrc.json"
|
||||
"!{projectRoot}/.eslintrc.json",
|
||||
"!{projectRoot}/**/*.stories.@(js|jsx|ts|tsx|mdx)",
|
||||
"!{projectRoot}/.storybook/**/*",
|
||||
"!{projectRoot}/tsconfig.storybook.json"
|
||||
],
|
||||
"sharedGlobals": ["{workspaceRoot}/babel.config.json"]
|
||||
},
|
||||
|
||||
15337
package-lock.json
generated
15337
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@@ -20,10 +20,20 @@
|
||||
"@nrwl/js": "15.9.2",
|
||||
"@nrwl/linter": "15.9.2",
|
||||
"@nrwl/next": "15.9.2",
|
||||
"@nrwl/react": "15.9.2",
|
||||
"@nrwl/react": "^15.9.2",
|
||||
"@nrwl/rollup": "15.9.2",
|
||||
"@nrwl/storybook": "15.9.2",
|
||||
"@nrwl/webpack": "15.9.2",
|
||||
"@nrwl/workspace": "15.9.2",
|
||||
"@nx/js": "16.6.0",
|
||||
"@rollup/plugin-url": "^7.0.0",
|
||||
"@storybook/addon-essentials": "7.0.18",
|
||||
"@storybook/addon-interactions": "7.0.18",
|
||||
"@storybook/core-server": "7.0.18",
|
||||
"@storybook/jest": "~0.1.0",
|
||||
"@storybook/react-webpack5": "^7.0.18",
|
||||
"@storybook/test-runner": "^0.11.0",
|
||||
"@storybook/testing-library": "~0.2.0",
|
||||
"@svgr/rollup": "^6.1.2",
|
||||
"@swc/core": "^1.2.173",
|
||||
"@swc/helpers": "~0.5.0",
|
||||
@@ -39,6 +49,7 @@
|
||||
"@typescript-eslint/parser": "^5.36.1",
|
||||
"babel-jest": "^29.4.1",
|
||||
"chai": "^4.3.7",
|
||||
"core-js": "^3.6.5",
|
||||
"cypress": "^12.2.0",
|
||||
"eslint": "~8.15.0",
|
||||
"eslint-config-next": "13.1.1",
|
||||
@@ -58,11 +69,16 @@
|
||||
"react-test-renderer": "18.2.0",
|
||||
"rehype-stringify": "^9.0.3",
|
||||
"remark": "^14.0.3",
|
||||
"storybook-tailwind-dark-mode": "^1.0.22",
|
||||
"swc-loader": "0.1.15",
|
||||
"ts-jest": "^29.0.5",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "~4.9.5",
|
||||
"unist-util-select": "^4.0.3",
|
||||
"unist-util-visit": "^4.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/components/.storybook/robots.txt
Normal file
3
packages/components/.storybook/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
User-agent: *
|
||||
Allow: /$
|
||||
Disallow: /
|
||||
@@ -16,7 +16,7 @@
|
||||
"build": "tsc && vite build && npm run build-tailwind && npm run fix-leaflet",
|
||||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build",
|
||||
"build-storybook": "storybook build && cp ./.storybook/robots.txt ./storybook-static",
|
||||
"build-tailwind": "NODE_ENV=production npx tailwindcss --postcss -c tailwind.config.js -i src/index.css -o ./dist/style.css --minify",
|
||||
"prepare": "npm run build",
|
||||
"fix-leaflet": "node ./scripts/fix-leaflet.cjs"
|
||||
|
||||
21
packages/core/.storybook/main.ts
Normal file
21
packages/core/.storybook/main.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { StorybookConfig } from '@storybook/react-webpack5';
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
|
||||
addons: [
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-interactions',
|
||||
'@nrwl/react/plugins/storybook',
|
||||
'storybook-tailwind-dark-mode'
|
||||
],
|
||||
framework: {
|
||||
name: '@storybook/react-webpack5',
|
||||
options: {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
// To customize your webpack configuration you can use the webpackFinal field.
|
||||
// Check https://storybook.js.org/docs/react/builders/webpack#extending-storybooks-webpack-config
|
||||
// and https://nx.dev/packages/storybook/documents/custom-builder-configs
|
||||
14
packages/core/.storybook/preview.ts
Normal file
14
packages/core/.storybook/preview.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import './tailwind-imports.css';
|
||||
|
||||
const preview = {
|
||||
globalTypes: {
|
||||
darkMode: {
|
||||
defaultValue: false, // Enable dark mode by default on all stories
|
||||
},
|
||||
className: {
|
||||
defaultValue: 'dark', // Set your custom dark mode class name
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
||||
3
packages/core/.storybook/tailwind-imports.css
Normal file
3
packages/core/.storybook/tailwind-imports.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
31
packages/core/.storybook/tsconfig.json
Normal file
31
packages/core/.storybook/tsconfig.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"emitDecoratorMetadata": true,
|
||||
"outDir": ""
|
||||
},
|
||||
"files": [
|
||||
"../../node_modules/@nx/react/typings/styled-jsx.d.ts",
|
||||
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
|
||||
"../../node_modules/@nx/react/typings/image.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.test.js"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.stories.ts",
|
||||
"src/**/*.stories.js",
|
||||
"src/**/*.stories.jsx",
|
||||
"src/**/*.stories.tsx",
|
||||
"src/**/*.stories.mdx",
|
||||
".storybook/*.js",
|
||||
".storybook/*.ts"
|
||||
]
|
||||
}
|
||||
11
packages/core/postcss.config.js
Normal file
11
packages/core/postcss.config.js
Normal file
@@ -0,0 +1,11 @@
|
||||
// libs/shared/ui/postcss.config.js
|
||||
const { join } = require('path');
|
||||
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {
|
||||
config: join(__dirname, 'tailwind.config.js'),
|
||||
},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
@@ -34,6 +34,37 @@
|
||||
"jestConfig": "packages/core/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
},
|
||||
"storybook": {
|
||||
"executor": "@nrwl/storybook:storybook",
|
||||
"options": {
|
||||
"port": 4400,
|
||||
"configDir": "packages/core/.storybook"
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {
|
||||
"quiet": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"build-storybook": {
|
||||
"executor": "@nrwl/storybook:build",
|
||||
"outputs": ["{options.outputDir}"],
|
||||
"options": {
|
||||
"outputDir": "dist/storybook/core",
|
||||
"configDir": "packages/core/.storybook"
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {
|
||||
"quiet": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"test-storybook": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"command": "test-storybook -c packages/core/.storybook --url=http://localhost:4400"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
48
packages/core/src/ui/Card/Card.stories.tsx
Normal file
48
packages/core/src/ui/Card/Card.stories.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { Card } from './Card';
|
||||
|
||||
const meta: Meta<typeof Card> = {
|
||||
component: Card,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Card>;
|
||||
|
||||
/*
|
||||
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
|
||||
* See https://storybook.js.org/docs/react/api/csf
|
||||
* to learn how to use render functions.
|
||||
*/
|
||||
|
||||
const blog = {
|
||||
urlPath: "#",
|
||||
title: "Card title goes here",
|
||||
date: "2021-01-01",
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, diam quis accumsan maximus, quam libero porttitor nisl, vita",
|
||||
}
|
||||
|
||||
export const Primary: Story = {
|
||||
render: () => (
|
||||
<Card className="md:col-span-3">
|
||||
<Card.Title href={`${blog.urlPath}`}>
|
||||
{blog.title}
|
||||
</Card.Title>
|
||||
<Card.Eyebrow
|
||||
as="time"
|
||||
dateTime={blog.date}
|
||||
className="md:hidden"
|
||||
decorate
|
||||
>
|
||||
{blog.date}
|
||||
</Card.Eyebrow>
|
||||
{blog.description && (
|
||||
<Card.Description>
|
||||
{blog.description}
|
||||
</Card.Description>
|
||||
)}
|
||||
<Card.Cta>Read article</Card.Cta>
|
||||
</Card>
|
||||
),
|
||||
};
|
||||
44
packages/core/tailwind.config.js
Normal file
44
packages/core/tailwind.config.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const { createGlobPatternsForDependencies } = require('@nrwl/next/tailwind');
|
||||
const { join } = require('path');
|
||||
// const defaultTheme = require("tailwindcss/defaultTheme");
|
||||
const colors = require("tailwindcss/colors");
|
||||
|
||||
module.exports = {
|
||||
content: [
|
||||
join(__dirname, './src/**/*.{js,ts,jsx,tsx}'),
|
||||
...createGlobPatternsForDependencies(__dirname),
|
||||
],
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
// support wider width for large screens >1440px eg. in hero
|
||||
maxWidth: {
|
||||
"8xl": "88rem",
|
||||
},
|
||||
// fontFamily: {
|
||||
// sans: ["ui-sans-serif", ...defaultTheme.fontFamily.sans],
|
||||
// serif: ["ui-serif", ...defaultTheme.fontFamily.serif],
|
||||
// mono: ["ui-monospace", ...defaultTheme.fontFamily.mono],
|
||||
// headings: ["-apple-system", ...defaultTheme.fontFamily.sans],
|
||||
// },
|
||||
colors: {
|
||||
background: {
|
||||
DEFAULT: colors.white,
|
||||
dark: colors.slate[900],
|
||||
},
|
||||
primary: {
|
||||
DEFAULT: colors.gray[700],
|
||||
dark: colors.gray[300],
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: colors.sky[400],
|
||||
dark: colors.sky[400],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
/* eslint global-require: off */
|
||||
// plugins: [
|
||||
// require("@tailwindcss/typography")
|
||||
// ],
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"jsx": "react-jsx",
|
||||
"module": "es2020",
|
||||
"moduleResolution": "node",
|
||||
@@ -22,6 +23,9 @@
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
},
|
||||
{
|
||||
"path": "./.storybook/tsconfig.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx"
|
||||
"**/*.test.jsx",
|
||||
"**/*.stories.ts",
|
||||
"**/*.stories.js",
|
||||
"**/*.stories.jsx",
|
||||
"**/*.stories.tsx"
|
||||
],
|
||||
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ const defaultWikiLinkResolver = (target: string) => {
|
||||
|
||||
export interface FromMarkdownOptions {
|
||||
pathFormat?:
|
||||
| "raw" // default; use for regular relative or absolute paths
|
||||
| "obsidian-absolute" // use for Obsidian-style absolute paths (with no leading slash)
|
||||
| "obsidian-short"; // use for Obsidian-style shortened paths (shortest path possible)
|
||||
| "raw" // default; use for regular relative or absolute paths
|
||||
| "obsidian-absolute" // use for Obsidian-style absolute paths (with no leading slash)
|
||||
| "obsidian-short"; // use for Obsidian-style shortened paths (shortest path possible)
|
||||
permalinks?: string[]; // list of permalinks to match possible permalinks of a wiki link against
|
||||
wikiLinkResolver?: (name: string) => string[]; // function to resolve wiki links to an array of possible permalinks
|
||||
newClassName?: string; // class name to add to links that don't have a matching permalink
|
||||
@@ -125,13 +125,24 @@ function fromMarkdown(opts: FromMarkdownOptions = {}) {
|
||||
if (isEmbed) {
|
||||
const [isSupportedFormat, format] = isSupportedFileFormat(target);
|
||||
if (!isSupportedFormat) {
|
||||
wikiLink.data.hName = "p";
|
||||
wikiLink.data.hChildren = [
|
||||
{
|
||||
type: "text",
|
||||
value: `![[${target}]]`,
|
||||
},
|
||||
];
|
||||
// Temporarily render note transclusion as a regular wiki link
|
||||
if (!format) {
|
||||
wikiLink.data.hName = "a";
|
||||
wikiLink.data.hProperties = {
|
||||
className: classNames + " " + "transclusion",
|
||||
href: hrefTemplate(link) + headingId,
|
||||
};
|
||||
wikiLink.data.hChildren = [{ type: "text", value: displayName }];
|
||||
|
||||
} else {
|
||||
wikiLink.data.hName = "p";
|
||||
wikiLink.data.hChildren = [
|
||||
{
|
||||
type: "text",
|
||||
value: `![[${target}]]`,
|
||||
},
|
||||
];
|
||||
}
|
||||
} else if (format === "pdf") {
|
||||
wikiLink.data.hName = "iframe";
|
||||
wikiLink.data.hProperties = {
|
||||
|
||||
@@ -15,9 +15,9 @@ const defaultWikiLinkResolver = (target: string) => {
|
||||
|
||||
export interface HtmlOptions {
|
||||
pathFormat?:
|
||||
| "raw" // default; use for regular relative or absolute paths
|
||||
| "obsidian-absolute" // use for Obsidian-style absolute paths (with no leading slash)
|
||||
| "obsidian-short"; // use for Obsidian-style shortened paths (shortest path possible)
|
||||
| "raw" // default; use for regular relative or absolute paths
|
||||
| "obsidian-absolute" // use for Obsidian-style absolute paths (with no leading slash)
|
||||
| "obsidian-short"; // use for Obsidian-style shortened paths (shortest path possible)
|
||||
permalinks?: string[]; // list of permalinks to match possible permalinks of a wiki link against
|
||||
wikiLinkResolver?: (name: string) => string[]; // function to resolve wiki links to an array of possible permalinks
|
||||
newClassName?: string; // class name to add to links that don't have a matching permalink
|
||||
@@ -108,7 +108,16 @@ function html(opts: HtmlOptions = {}) {
|
||||
if (isEmbed) {
|
||||
const [isSupportedFormat, format] = isSupportedFileFormat(target);
|
||||
if (!isSupportedFormat) {
|
||||
this.raw(`![[${target}]]`);
|
||||
// Temporarily render note transclusion as a regular wiki link
|
||||
if (!format) {
|
||||
this.tag(
|
||||
`<a href="${hrefTemplate(link + headingId)}" class="${classNames} transclusion">`
|
||||
);
|
||||
this.raw(displayName);
|
||||
this.tag("</a>");
|
||||
} else {
|
||||
this.raw(`![[${target}]]`);
|
||||
}
|
||||
} else if (format === "pdf") {
|
||||
this.tag(
|
||||
`<iframe width="100%" src="${hrefTemplate(
|
||||
|
||||
@@ -309,4 +309,16 @@ describe("micromark-extension-wiki-link", () => {
|
||||
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", {
|
||||
extensions: [syntax()],
|
||||
htmlExtensions: [html() as any], // TODO type fix
|
||||
});
|
||||
expect(serialized).toBe(
|
||||
'<p><a href="Some Page" class="internal new transclusion">Some Page</a></p>'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -535,4 +535,28 @@ describe("remark-wiki-link", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("transclusions", () => {
|
||||
test("replaces a transclusion with a regular wiki link", () => {
|
||||
const processor = unified().use(markdown).use(wikiLinkPlugin);
|
||||
|
||||
let ast = processor.parse("![[Some Page]]");
|
||||
ast = processor.runSync(ast);
|
||||
|
||||
expect(select("wikiLink", ast)).not.toEqual(null);
|
||||
|
||||
visit(ast, "wikiLink", (node: Node) => {
|
||||
expect(node.data?.isEmbed).toEqual(true);
|
||||
expect(node.data?.exists).toEqual(false);
|
||||
expect(node.data?.permalink).toEqual("Some Page");
|
||||
expect(node.data?.alias).toEqual(null);
|
||||
expect(node.data?.hName).toEqual("a");
|
||||
expect((node.data?.hProperties as any).className).toEqual(
|
||||
"internal new transclusion"
|
||||
);
|
||||
expect((node.data?.hProperties as any).href).toEqual("Some Page");
|
||||
expect((node.data?.hChildren as any)[0].value).toEqual("Some Page");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useEffect, useState } from 'react';
|
||||
const Stat = ({ title, value, ...props }) => {
|
||||
return (
|
||||
<div {...props}>
|
||||
<span className="text-6xl font-bold text-secondary">{value}</span>
|
||||
<span className="text-4xl sm:text-6xl font-bold text-secondary">{value}</span>
|
||||
<p className="text-lg font-medium">{title}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -86,7 +86,7 @@ export default function Layout({
|
||||
<>
|
||||
{title && <NextSeo title={title} description={description} />}
|
||||
<Nav />
|
||||
<div className="mx-auto p-6 bg-background dark:bg-background-dark">
|
||||
<div className="mx-auto p-2 sm:p-6 bg-background dark:bg-background-dark">
|
||||
{isHomePage && <Hero />}
|
||||
|
||||
<div className="relative mx-auto flex max-w-8xl justify-center sm:px-2 lg:px-8 xl:px-12">
|
||||
|
||||
@@ -92,7 +92,7 @@ export default function MobileNavigation({ navigation }) {
|
||||
>
|
||||
{/* <Logomark className="h-9 w-9" /> */}
|
||||
<div className="font-extrabold text-slate-900 dark:text-white text-2xl ml-6">
|
||||
{siteConfig.title}
|
||||
PortalJS
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -95,7 +95,10 @@ export default function Nav() {
|
||||
<MobileNavigation navigation={siteConfig.navLinks} />
|
||||
</div>
|
||||
<div className="flex flex-none items-center">
|
||||
<NavbarTitle />
|
||||
<div className="hidden sm:block">
|
||||
<NavbarTitle />
|
||||
</div>
|
||||
|
||||
<div className="hidden lg:flex ml-8 mr-6 sm:mr-8 md:mr-0">
|
||||
{siteConfig.navLinks.map((item) => (
|
||||
<NavItem item={item} key={item.name} />
|
||||
@@ -123,9 +126,8 @@ export default function Nav() {
|
||||
)}
|
||||
{siteConfig.github && (
|
||||
<div className="mt-1">
|
||||
<
|
||||
// @ts-ignore
|
||||
GitHubButton
|
||||
{/* @ts-ignore */}
|
||||
<GitHubButton
|
||||
href={siteConfig.github}
|
||||
data-color-scheme="no-preference: light; light: light; dark: dark;"
|
||||
data-size="large"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: 'Creating new datasets'
|
||||
description: 'PortalJS Tutorial II - Learn how to create new datasets on a data portal'
|
||||
description: 'Learn how to create new datasets and an index for all datasets on a data portal built with PortalJS'
|
||||
---
|
||||
|
||||
So far, the PortalJS app we created only has a single page displaying a dataset. Data catalogs and data portals generally showcase many different datasets.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<NextSeo title="Deploying your PortalJS app - PortalJS" />
|
||||
|
||||
# Deploying your PortalJS app
|
||||
---
|
||||
title: Deploying your PortalJS app
|
||||
description: 'Learn how to deploy PortalJS apps to Vercel and Cloudflare'
|
||||
---
|
||||
|
||||
Finally, let's learn how to deploy PortalJS apps to Vercel or Cloudflare Pages.
|
||||
|
||||
@@ -35,7 +36,7 @@ When you deploy, your PortalJS app will start building. It should finish in unde
|
||||
|
||||
When it’s done, you’ll get deployment URLs. Click on one of the URLs and you should see your PortalJS app live.
|
||||
|
||||
>[!tip]
|
||||
> [!tip]
|
||||
> You can find a more in-depth explanation about this process at https://nextjs.org/learn/basics/deploying-nextjs-app/deploy
|
||||
|
||||
### One-Click Deploy
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Getting Started
|
||||
description: 'Getting started guide and tutorial about data portal-building with PortalJS'
|
||||
description: 'Getting started guide and tutorial about data portal-building with PortalJS!'
|
||||
---
|
||||
|
||||
Welcome to the PortalJS documentation!
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<NextSeo title="Searching datasets - PortalJS" />
|
||||
|
||||
# Searching datasets
|
||||
---
|
||||
title: Searching datasets
|
||||
description: 'Learn how to create a searchable datasets index with facets on a PortalJs data portal'
|
||||
---
|
||||
|
||||
Typing out every link in the index page will get cumbersome eventually, and as the portal grows, finding the datasets you are looking for on the index page will become harder and harder, for that we will need search functionality.
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<NextSeo title="Showing metadata - PortalJS" />
|
||||
|
||||
# Showing metadata
|
||||
---
|
||||
title: Showing metadata
|
||||
description: "Learn how to display metadata on the dataset page of a data portal built with PortalJS"
|
||||
---
|
||||
|
||||
If you go now to `http://localhost:3000/my-awesome-dataset`, you will see that we now have two titles on the page. That's because `title` is one of the default metadata fields supported by PortalJS.
|
||||
|
||||
|
||||
@@ -135,6 +135,56 @@ By the end of this tutorial, you will:
|
||||
> [!tip]
|
||||
> Read full tutorial [[collaborate-on-website-project|here!]] (TBD)
|
||||
|
||||
### Tutorial 4: Customising your website locally and previewing your changes locally
|
||||
|
||||
In this tutorial, we will dive into the more technical aspects of website customisation, but this time, everything will be done locally. You'll learn how to preview your site on your own machine before pushing changes to the live site, ensuring everything looks and works exactly as you want.
|
||||
|
||||
By the end of this tutorial, you will:
|
||||
|
||||
- Understand how to preview your site locally.
|
||||
- Know how to change your website's title and description.
|
||||
- Learn how to customise your website's appearance using Tailwind themes.
|
||||
- Understand how to configure the navbar, navigation links, and logo.
|
||||
- Learn how to integrate Google Analytics into your website.
|
||||
- Be aware of additional customisation options for deeper customisation.
|
||||
|
||||
#### Previewing the site locally
|
||||
|
||||
- Navigate to your website's repository directory on your computer using command line
|
||||
- Install the site's dependencies
|
||||
- Start the local development server
|
||||
- Visit the local address displayed in your command line. Yay! You can now preview your changes locally, live! 🎉
|
||||
|
||||
#### Changing the site title and description
|
||||
|
||||
- Perfect! You've personalised your site's title and description! 🎉
|
||||
|
||||
#### Configuring the title, description and navbar
|
||||
|
||||
- Open the `content/config.mjs` file in your code editor
|
||||
- Edit the `title` and `description` fields
|
||||
- Edit the fields in the `navbar` field to customise your navbar's title and logo. Then, add navigation links to `navLinks` field (these will be displayed in the navbar). Save and refresh your local site to see the changes.
|
||||
|
||||
#### Integrating with Google Analytics
|
||||
|
||||
- Still in the `content/config.mjs` file, navigate to the `analytics` field
|
||||
- Enter your Google Analytics tracking ID, save and refresh your local site to ensure it's integrated correctly
|
||||
- Excellent! Your website is now integrated with Google Analytics! 🎉
|
||||
|
||||
#### Customising the Tailwind theme
|
||||
|
||||
- Open `tailwind.config.js` file in your code editor
|
||||
- Change the light and dark theme colours and fonts to your liking, save and refresh your local site to see the changes
|
||||
- Awesome! Your website now has a new look! 🎉
|
||||
|
||||
When you're happy with all your changes, use GitHub Desktop to commit your changes and push them back to your GitHub repository.
|
||||
|
||||
In addition to these topics, the full tutorial shows you what other customisations options are available and where to find information on these.
|
||||
|
||||
> [!tip]
|
||||
> Read full tutorial here! (TBD)
|
||||
|
||||
|
||||
## Howtos
|
||||
|
||||
- [[quickly-create-a-sandbox-website|How to quickly create a sandbox website]]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: How to add Google Analytics?
|
||||
description: Learn to implement Google Analytics on PortalJS data portals
|
||||
description: Learn how to implement Google Analytics on PortalJS data portals
|
||||
---
|
||||
|
||||
>[!todo] Prerequisites
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: How to add a simple blog?
|
||||
description: How to add a simple blog on a PortalJS data portal
|
||||
description: Learn how to add a simple blog on a PortalJS data portal
|
||||
---
|
||||
|
||||
## Setup
|
||||
@@ -16,15 +16,15 @@ npm i @portaljs/core
|
||||
Add the following code to the Next.js page that is going to be your blog home page, e.g. to `/pages/blog/index.tsx`:
|
||||
|
||||
```tsx
|
||||
import { BlogsList, SimpleLayout } from "@portaljs/core";
|
||||
import { BlogsList, SimpleLayout } from '@portaljs/core';
|
||||
|
||||
// pass a list of blogs, home page title and home page description, e.g. from `getStaticProps`
|
||||
export default function BlogIndex({ blogs, title, description }) {
|
||||
return (
|
||||
<SimpleLayout title={title} description={description}>
|
||||
<BlogsList blogs={blogs} />
|
||||
</SimpleLayout>
|
||||
);
|
||||
return (
|
||||
<SimpleLayout title={title} description={description}>
|
||||
<BlogsList blogs={blogs} />
|
||||
</SimpleLayout>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -32,16 +32,16 @@ export default function BlogIndex({ blogs, title, description }) {
|
||||
|
||||
```ts
|
||||
interface BlogsListProps {
|
||||
blogs: Blog;
|
||||
blogs: Blog;
|
||||
}
|
||||
|
||||
interface Blog {
|
||||
title: string;
|
||||
date: string;
|
||||
urlPath: string;
|
||||
description?: string;
|
||||
authors?: Array<string>;
|
||||
tags?: Array<string>;
|
||||
title: string;
|
||||
date: string;
|
||||
urlPath: string;
|
||||
description?: string;
|
||||
authors?: Array<string>;
|
||||
tags?: Array<string>;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -57,7 +57,7 @@ export default BlogPost({ content, title, date, authors }) {
|
||||
<BlogLayout title={title} date={date} authors={authors}
|
||||
{content}
|
||||
</BlogLayout>
|
||||
)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -65,14 +65,14 @@ export default BlogPost({ content, title, date, authors }) {
|
||||
|
||||
```ts
|
||||
interface BlogLayoutProps {
|
||||
title?: string;
|
||||
date?: string;
|
||||
authors?: Array<Author>;
|
||||
title?: string;
|
||||
date?: string;
|
||||
authors?: Array<Author>;
|
||||
}
|
||||
|
||||
interface Author {
|
||||
name: string;
|
||||
avatar: string; // avatar image path
|
||||
urlPath?: string; // author page
|
||||
name: string;
|
||||
avatar: string; // avatar image path
|
||||
urlPath?: string; // author page
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: How to customize page metadata for SEO?
|
||||
description: Learn to customize page metadata for SEO on data portals with PortalJS
|
||||
description: Learn how to customize page metadata for SEO on data portals built with PortalJS
|
||||
---
|
||||
|
||||
>[!info]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: How to build a sitemap?
|
||||
description: Learn how to build a sitemap for a data portal with PortalJS
|
||||
description: Learn how to build a sitemap for a data portal built with PortalJS
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -2,4 +2,10 @@
|
||||
module.exports = {
|
||||
siteUrl: process.env.SITE_URL || 'https://portaljs.org',
|
||||
generateRobotsTxt: true,
|
||||
}
|
||||
robotsTxtOptions: {
|
||||
policies: [
|
||||
{ userAgent: '*', disallow: '/people' },
|
||||
{ userAgent: '*', disallow: '/?amp=1' },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
24
site/pages/404.tsx
Normal file
24
site/pages/404.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import Layout from '@/components/Layout';
|
||||
import { NextSeo } from 'next-seo';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function () {
|
||||
return (
|
||||
<>
|
||||
<NextSeo noindex={true} nofollow={true} />
|
||||
<Layout>
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="text-center">
|
||||
<h1 className="text-2xl">404 - Page not found</h1>
|
||||
<p className="text-lg mt-5">
|
||||
It seems like you are looking for a page that doesn't exist.
|
||||
</p>
|
||||
<Link className="underline" href="/">
|
||||
Go back to home
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import { CustomAppProps } from './_app.jsx';
|
||||
import computeFields from '@/lib/computeFields';
|
||||
import { getAuthorsDetails } from '@/lib/getAuthorsDetails';
|
||||
import JSONLD from '@/components/JSONLD';
|
||||
import { BreadcrumbJsonLd } from 'next-seo';
|
||||
|
||||
export default function Page({ source, meta, sidebarTree }) {
|
||||
source = JSON.parse(source);
|
||||
@@ -29,9 +30,19 @@ export default function Page({ source, meta, sidebarTree }) {
|
||||
setTableOfContents(toc ?? []);
|
||||
}, [router.asPath]); // update table of contents on route change with next/link
|
||||
|
||||
const urlSegments = meta.urlPath.split('/');
|
||||
const breadcrumbs = urlSegments.map((segment, i) => {
|
||||
return {
|
||||
position: i + 1,
|
||||
name: i == urlSegments.length - 1 ? meta.title || segment : segment,
|
||||
item: '/' + urlSegments.slice(0, i + 1).join('/'),
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<JSONLD meta={meta} source={source.compiledSource} />
|
||||
<BreadcrumbJsonLd itemListElements={breadcrumbs} />
|
||||
<Layout
|
||||
tableOfContents={tableOfContents}
|
||||
title={meta.title}
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
"skipDefaultLibCheck": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@portaljs/core": ["packages/core/src/index.ts"],
|
||||
"@portaljs/portaljs-components": [
|
||||
"packages/portaljs-components/src/index.ts"
|
||||
],
|
||||
"@portaljs/core": ["packages/core/src/index.ts"]
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "tmp"]
|
||||
|
||||
Reference in New Issue
Block a user