mirror of
https://github.com/lowdefy/lowdefy.git
synced 2025-03-31 15:20:32 +08:00
Merge pull request #1094 from lowdefy/link-component
Add Lowdefy Link component for blocks.
This commit is contained in:
commit
d0897303db
@ -32,6 +32,10 @@
|
||||
"scripts": {
|
||||
"build": "lerna run build",
|
||||
"build:dev": "NODE_ENV=development lerna run build",
|
||||
"build:plugins": "yarn build:blocks && yarn build:connections && yarn build:operators",
|
||||
"build:blocks": "lerna run --scope '@lowdefy/blocks-*' build",
|
||||
"build:connections": "lerna run --scope '@lowdefy/connection-*' build",
|
||||
"build:operators": "lerna run --scope '@lowdefy/operators-*' build",
|
||||
"clean": "lerna run clean",
|
||||
"lerna:version": "lerna version --no-git-tag-version",
|
||||
"lerna:publish": "lerna publish from-git",
|
||||
@ -40,6 +44,7 @@
|
||||
"start:server-dev": "yarn workspace @lowdefy/server-dev start --package-manager yarn --config-directory ../../app",
|
||||
"start": "yarn workspace @lowdefy/server build:lowdefy --config-directory ../../app && yarn && yarn workspace @lowdefy/server build:next && yarn workspace @lowdefy/server start",
|
||||
"start:dev": "yarn workspace @lowdefy/server build:lowdefy --config-directory ../../app && yarn && yarn workspace @lowdefy/server dev",
|
||||
"start:dev-docs": "yarn workspace @lowdefy/server build:lowdefy --config-directory ../docs && yarn && yarn workspace @lowdefy/server dev",
|
||||
"test": "lerna run test"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -39,6 +39,11 @@ async function validateConfig({ components }) {
|
||||
if (type.isNone(components.config.theme)) {
|
||||
components.config.theme = {};
|
||||
}
|
||||
if (type.isString(components.config.basePath)) {
|
||||
if (components.config.basePath[0] !== '/') {
|
||||
throw Error('Base path must start with "/".');
|
||||
}
|
||||
}
|
||||
validate({
|
||||
schema: lowdefySchema.definitions.authConfig,
|
||||
data: components.config.auth,
|
||||
|
@ -155,3 +155,31 @@ test('validateConfig config error when protected or public are false.', async ()
|
||||
'Public pages can not be set to false.'
|
||||
);
|
||||
});
|
||||
|
||||
test('validateConfig config error when basePath does not start with "/".', async () => {
|
||||
let components = {
|
||||
config: {
|
||||
basePath: '/base',
|
||||
},
|
||||
};
|
||||
const result = await validateConfig({ components, context });
|
||||
expect(result).toEqual({
|
||||
config: {
|
||||
auth: {
|
||||
pages: {
|
||||
roles: {},
|
||||
},
|
||||
},
|
||||
basePath: '/base',
|
||||
theme: {},
|
||||
},
|
||||
});
|
||||
components = {
|
||||
config: {
|
||||
basePath: 'base',
|
||||
},
|
||||
};
|
||||
await expect(validateConfig({ components, context })).rejects.toThrow(
|
||||
'Base path must start with "/".'
|
||||
);
|
||||
});
|
||||
|
@ -598,6 +598,13 @@ export default {
|
||||
auth: {
|
||||
$ref: '#/definitions/authConfig',
|
||||
},
|
||||
basePath: {
|
||||
type: 'string',
|
||||
description: 'App base path to apply to all routes. Base path must start with "/".',
|
||||
errorMessage: {
|
||||
type: 'App "config.basePath" should be a string.',
|
||||
},
|
||||
},
|
||||
homePageId: {
|
||||
type: 'string',
|
||||
description:
|
||||
|
@ -123,6 +123,20 @@ _ref:
|
||||
|
||||
- `basePath: string`: The base path setting for the application. This variable is used to prefix route paths for example `${basePath}/public/logo-square-light-theme.png`. The default base path is ''.
|
||||
- `blockId: string`: The block's id within the Lowdefy app, this is useful for setting a unique `id` on DOM elements.
|
||||
- `components: object`: Helper React components that are exposed to blocks to use internally.
|
||||
- `Icon`: component`: Lowdefy standard Icon React component to render build in icons.
|
||||
- `Link`: component`: Lowdefy standard Link React component used as links to pages or external urls. The following props apply:
|
||||
- `ariaLabel: string`: Arial-label to apply to link tag.
|
||||
- `back: boolean`: When the link is clicked, trigger the browser back.
|
||||
- `home: boolean`: When the link is clicked, route to the home page.
|
||||
- `input: object`: When the link is clicked, pass data as the input object to the next Lowdefy page. Can only be used with pageId link and newTab false. See [Input]( TODO: Link to input page? ).
|
||||
- `newTab: boolean`: When the link is clicked, open the page in a new browser tab.
|
||||
- `pageId: string`: When the link is clicked, route to the provided Lowdefy page.
|
||||
- `rel: string`: Overwrite `<a/>` tag property.
|
||||
- `replace: boolean`: Prevent adding a new entry into browser history by replacing the url instead of pushing into history. Can only be used with pageId link and newTab false.
|
||||
- `scroll: boolean`: Disable scrolling to the top of the page after page transition. Can only be used with pageId link and newTab false.
|
||||
- `url: string`: When the link is clicked, route to an external url.
|
||||
- `urlQuery: object`: When the link is clicked, pass data as a url query to the next Lowdefy page. See [url query]( TODO: Link to url query page? ).
|
||||
- `content: object`: Passed to `container` and `context` block categories. The `content` object with methods to render sub blocks into content areas. The method name is the same as the area key, for example, 'content.content()` renders a blocks default `content` area.
|
||||
- `events: object`: All events defined on the block in the Lowdefy app config.
|
||||
- `[event_key]: object`: Event keys gives a handle name to the event details.
|
||||
|
@ -42,6 +42,7 @@ _ref:
|
||||
|
||||
The config object has the following properties:
|
||||
|
||||
- `basePath: string`: Set the base path to serve the Lowdefy application from. This will route all pages under `https://example.com/<base-path>/<page-id>` instead of the default `https://example.com/<page-id>`. The basePath value must start with "/".
|
||||
- `homePageId: string`: The pageId of the page that should be loaded when a user loads the app without a pageId in the url route. This is the page that is loaded when you navigate to `yourdomain.com`.
|
||||
- `experimental_initPageId: string`: The pageId of the page that should be loaded when app is initialized. User is then redirected to requeted page. You can use onInit/onInitAsync/onEnter/onEnterAsync events to fetch and prepare global variables for other parts of the app.
|
||||
|
||||
|
@ -32,7 +32,7 @@ _ref:
|
||||
|
||||
The Lowdefy server can be configured using the following environment variables:
|
||||
|
||||
- `LOWDEFY_SERVER_BASE_PATH`: Set the base path to serve the Lowdefy application from. This will serve the application under `https://example.com/<base-path>`instead of `https://example.com`, and all pages under `https://example.com/<base-path>/<page-id>` instead of the default `https://example.com/<page-id>`.
|
||||
- `LOWDEFY_BASE_PATH`: Set the base path to serve the Lowdefy application from. This will serve the application under `https://example.com/<base-path>`instead of `https://example.com`, and all pages under `https://example.com/<base-path>/<page-id>` instead of the default `https://example.com/<page-id>`.
|
||||
- `LOWDEFY_SERVER_BUILD_DIRECTORY`: The directory of the built Lowdefy configuration (The output of `lowdefy build`, usually found at `./.lowdefy/build` in your project repository). The default is `./build` (or `/home/node/lowdefy/build`).
|
||||
- `LOWDEFY_SERVER_PUBLIC_DIRECTORY`: The directory of the public assets to be served. The default is `./public` (or `/home/node/lowdefy/public`).
|
||||
- `LOWDEFY_SERVER_PORT`: The port (inside the container) at which to run the server. The default is `3000`.
|
||||
|
@ -105,7 +105,7 @@ _ref:
|
||||
|
||||
The following environment variables can be specified:
|
||||
|
||||
- `LOWDEFY_SERVER_BASE_PATH`: Set the base path to serve the Lowdefy application from. This will serve the application under `https://example.com/<base-path>`instead of `https://example.com`, and all pages under `https://example.com/<base-path>/<page-id>` instead of the default `https://example.com/<page-id>`.
|
||||
- `LOWDEFY_BASE_PATH`: Set the base path to serve the Lowdefy application from. This will serve the application under `https://example.com/<base-path>`instead of `https://example.com`, and all pages under `https://example.com/<base-path>/<page-id>` instead of the default `https://example.com/<page-id>`.
|
||||
- `LOWDEFY_SERVER_BUILD_DIRECTORY`: The directory of the built Lowdefy configuration (The output of `lowdefy build`, usually found at `./.lowdefy/build` in your project repository). The default is `./.lowdefy/build`.
|
||||
- `LOWDEFY_SERVER_PORT`: The port at which to run the server. The default is `3000`.
|
||||
- `LOWDEFY_SERVER_PUBLIC_DIRECTORY`: The directory of the public assets to be served. The default is `./public`.
|
||||
|
@ -16,32 +16,42 @@
|
||||
|
||||
import { type, urlQuery as urlQueryFn } from '@lowdefy/helpers';
|
||||
|
||||
function createLink({ backLink, lowdefy, newOriginLink, sameOriginLink }) {
|
||||
function link({ back, home, input, newTab, pageId, url, urlQuery }) {
|
||||
let pathname = pageId;
|
||||
if (back) {
|
||||
return backLink();
|
||||
function createLink({ backLink, disabledLink, lowdefy, newOriginLink, noLink, sameOriginLink }) {
|
||||
function link(props) {
|
||||
if (props.disabled === true) {
|
||||
return disabledLink(props);
|
||||
}
|
||||
const lowdefyUrlQuery = type.isNone(urlQuery) ? '' : `?${urlQueryFn.stringify(urlQuery)}`;
|
||||
if (home) {
|
||||
if (lowdefy.home.configured) {
|
||||
pathname = '';
|
||||
pageId = lowdefy.home.pageId;
|
||||
} else {
|
||||
pathname = lowdefy.home.pageId;
|
||||
pageId = lowdefy.home.pageId;
|
||||
}
|
||||
if ([!props.pageId, !props.back, !props.home, !props.url].filter((v) => !v).length > 1) {
|
||||
throw Error(
|
||||
`Invalid Link: To avoid ambiguity, only one of 'back', 'home', 'pageId' or 'url' can be defined.`
|
||||
);
|
||||
}
|
||||
if (!type.isNone(pathname)) {
|
||||
if (!type.isNone(input)) {
|
||||
lowdefy.inputs[pageId] = input;
|
||||
}
|
||||
return sameOriginLink(`/${pathname}${lowdefyUrlQuery}`, newTab);
|
||||
if (props.back === true) {
|
||||
// Cannot set input or urlQuery on back
|
||||
return backLink(props);
|
||||
}
|
||||
if (!type.isNone(url)) {
|
||||
return newOriginLink(`${url}${lowdefyUrlQuery}`, newTab);
|
||||
const lowdefyUrlQuery = type.isNone(props.urlQuery)
|
||||
? ''
|
||||
: `?${urlQueryFn.stringify(props.urlQuery)}`;
|
||||
if (props.home === true) {
|
||||
lowdefy.inputs[`page:${lowdefy.home.pageId}`] = props.input || {};
|
||||
return sameOriginLink({
|
||||
href: `/${lowdefy.home.configured ? '' : lowdefy.home.pageId}${lowdefyUrlQuery}`,
|
||||
...props,
|
||||
});
|
||||
}
|
||||
throw new Error(`Invalid Link.`);
|
||||
if (type.isString(props.pageId)) {
|
||||
lowdefy.inputs[`page:${props.pageId}`] = props.input || {};
|
||||
return sameOriginLink({ href: `/${props.pageId}${lowdefyUrlQuery}`, ...props });
|
||||
}
|
||||
if (type.isString(props.url)) {
|
||||
const protocol = props.url.includes(':') ? '' : 'https://';
|
||||
return newOriginLink({
|
||||
href: `${protocol}${props.url}${lowdefyUrlQuery}`,
|
||||
...props,
|
||||
});
|
||||
}
|
||||
return noLink(props);
|
||||
}
|
||||
return link;
|
||||
}
|
||||
|
@ -1,12 +1,16 @@
|
||||
import createLink from '../src/createLink.js';
|
||||
|
||||
const mockBackLink = jest.fn();
|
||||
const mockDisabledLink = jest.fn();
|
||||
const mockNewOriginLink = jest.fn();
|
||||
const mockNoLink = jest.fn();
|
||||
const mockSameOriginLink = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
mockBackLink.mockReset();
|
||||
mockDisabledLink.mockReset();
|
||||
mockNewOriginLink.mockReset();
|
||||
mockNoLink.mockReset();
|
||||
mockSameOriginLink.mockReset();
|
||||
});
|
||||
|
||||
@ -14,17 +18,34 @@ test('createLink, link with pageId', () => {
|
||||
const lowdefy = { inputs: {} };
|
||||
const link = createLink({
|
||||
backLink: mockBackLink,
|
||||
disabledLink: mockDisabledLink,
|
||||
lowdefy,
|
||||
newOriginLink: mockNewOriginLink,
|
||||
noLink: mockNoLink,
|
||||
sameOriginLink: mockSameOriginLink,
|
||||
});
|
||||
link({ pageId: 'page_1' });
|
||||
link({ pageId: 'page_1', urlQuery: { p: 3 } });
|
||||
expect(mockBackLink.mock.calls).toEqual([]);
|
||||
expect(mockDisabledLink.mock.calls).toEqual([]);
|
||||
expect(mockNewOriginLink.mock.calls).toEqual([]);
|
||||
expect(mockNoLink.mock.calls).toEqual([]);
|
||||
expect(mockSameOriginLink.mock.calls).toEqual([
|
||||
['/page_1', undefined],
|
||||
['/page_1?p=3', undefined],
|
||||
[
|
||||
{
|
||||
href: '/page_1',
|
||||
pageId: 'page_1',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
href: '/page_1?p=3',
|
||||
pageId: 'page_1',
|
||||
urlQuery: {
|
||||
p: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
@ -32,17 +53,36 @@ test('createLink, link with pageId new tab', () => {
|
||||
const lowdefy = { inputs: {} };
|
||||
const link = createLink({
|
||||
backLink: mockBackLink,
|
||||
disabledLink: mockDisabledLink,
|
||||
lowdefy,
|
||||
newOriginLink: mockNewOriginLink,
|
||||
noLink: mockNoLink,
|
||||
sameOriginLink: mockSameOriginLink,
|
||||
});
|
||||
link({ pageId: 'page_1', newTab: true });
|
||||
link({ pageId: 'page_1', newTab: true, urlQuery: { p: 3 } });
|
||||
expect(mockBackLink.mock.calls).toEqual([]);
|
||||
expect(mockDisabledLink.mock.calls).toEqual([]);
|
||||
expect(mockNewOriginLink.mock.calls).toEqual([]);
|
||||
expect(mockNoLink.mock.calls).toEqual([]);
|
||||
expect(mockSameOriginLink.mock.calls).toEqual([
|
||||
['/page_1', true],
|
||||
['/page_1?p=3', true],
|
||||
[
|
||||
{
|
||||
href: '/page_1',
|
||||
pageId: 'page_1',
|
||||
newTab: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
href: '/page_1?p=3',
|
||||
pageId: 'page_1',
|
||||
newTab: true,
|
||||
urlQuery: {
|
||||
p: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
@ -50,145 +90,366 @@ test('createLink, link with pageId with inputs', () => {
|
||||
const lowdefy = { inputs: {} };
|
||||
const link = createLink({
|
||||
backLink: mockBackLink,
|
||||
disabledLink: mockDisabledLink,
|
||||
lowdefy,
|
||||
newOriginLink: mockNewOriginLink,
|
||||
noLink: mockNoLink,
|
||||
sameOriginLink: mockSameOriginLink,
|
||||
});
|
||||
link({ pageId: 'page_1', input: { a: 1 } });
|
||||
link({ pageId: 'page_2', input: { a: 2 }, urlQuery: { p: 3 } });
|
||||
expect(mockBackLink.mock.calls).toEqual([]);
|
||||
expect(mockDisabledLink.mock.calls).toEqual([]);
|
||||
expect(mockNewOriginLink.mock.calls).toEqual([]);
|
||||
expect(mockNoLink.mock.calls).toEqual([]);
|
||||
expect(mockSameOriginLink.mock.calls).toEqual([
|
||||
['/page_1', undefined],
|
||||
['/page_2?p=3', undefined],
|
||||
[
|
||||
{
|
||||
href: '/page_1',
|
||||
input: {
|
||||
a: 1,
|
||||
},
|
||||
pageId: 'page_1',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
href: '/page_2?p=3',
|
||||
input: {
|
||||
a: 2,
|
||||
},
|
||||
pageId: 'page_2',
|
||||
urlQuery: {
|
||||
p: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
]);
|
||||
expect(lowdefy.inputs).toEqual({
|
||||
page_1: { a: 1 },
|
||||
page_2: { a: 2 },
|
||||
'page:page_1': { a: 1 },
|
||||
'page:page_2': { a: 2 },
|
||||
});
|
||||
});
|
||||
|
||||
test('createLink, link with url', () => {
|
||||
test('createLink, link with url and protocol', () => {
|
||||
const lowdefy = { inputs: {} };
|
||||
const link = createLink({
|
||||
backLink: mockBackLink,
|
||||
disabledLink: mockDisabledLink,
|
||||
lowdefy,
|
||||
newOriginLink: mockNewOriginLink,
|
||||
noLink: mockNoLink,
|
||||
sameOriginLink: mockSameOriginLink,
|
||||
});
|
||||
link({ url: 'http://localhost:8080/test' });
|
||||
link({ url: 'http://localhost:8080/test', urlQuery: { p: 3 } });
|
||||
expect(mockBackLink.mock.calls).toEqual([]);
|
||||
expect(mockDisabledLink.mock.calls).toEqual([]);
|
||||
expect(mockNoLink.mock.calls).toEqual([]);
|
||||
expect(mockNewOriginLink.mock.calls).toEqual([
|
||||
['http://localhost:8080/test', undefined],
|
||||
['http://localhost:8080/test?p=3', undefined],
|
||||
[
|
||||
{
|
||||
href: 'http://localhost:8080/test',
|
||||
url: 'http://localhost:8080/test',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
href: 'http://localhost:8080/test?p=3',
|
||||
url: 'http://localhost:8080/test',
|
||||
urlQuery: {
|
||||
p: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
]);
|
||||
expect(mockSameOriginLink.mock.calls).toEqual([]);
|
||||
});
|
||||
|
||||
test('createLink, link with url new tab', () => {
|
||||
test('createLink, link with url new tab and protocol', () => {
|
||||
const lowdefy = { inputs: {} };
|
||||
const link = createLink({
|
||||
backLink: mockBackLink,
|
||||
disabledLink: mockDisabledLink,
|
||||
lowdefy,
|
||||
newOriginLink: mockNewOriginLink,
|
||||
noLink: mockNoLink,
|
||||
sameOriginLink: mockSameOriginLink,
|
||||
});
|
||||
link({ url: 'http://localhost:8080/test', newTab: true });
|
||||
link({ url: 'http://localhost:8080/test', newTab: true, urlQuery: { p: 3 } });
|
||||
expect(mockBackLink.mock.calls).toEqual([]);
|
||||
expect(mockDisabledLink.mock.calls).toEqual([]);
|
||||
expect(mockNoLink.mock.calls).toEqual([]);
|
||||
expect(mockNewOriginLink.mock.calls).toEqual([
|
||||
['http://localhost:8080/test', true],
|
||||
['http://localhost:8080/test?p=3', true],
|
||||
[
|
||||
{
|
||||
href: 'http://localhost:8080/test',
|
||||
url: 'http://localhost:8080/test',
|
||||
newTab: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
href: 'http://localhost:8080/test?p=3',
|
||||
url: 'http://localhost:8080/test',
|
||||
urlQuery: {
|
||||
p: 3,
|
||||
},
|
||||
newTab: true,
|
||||
},
|
||||
],
|
||||
]);
|
||||
expect(mockSameOriginLink.mock.calls).toEqual([]);
|
||||
});
|
||||
|
||||
test('createLink, link with home', () => {
|
||||
const lowdefy = { inputs: {}, homePageId: 'home' };
|
||||
test('createLink, link with url and no protocol', () => {
|
||||
const lowdefy = { inputs: {} };
|
||||
const link = createLink({
|
||||
backLink: mockBackLink,
|
||||
disabledLink: mockDisabledLink,
|
||||
lowdefy,
|
||||
newOriginLink: mockNewOriginLink,
|
||||
noLink: mockNoLink,
|
||||
sameOriginLink: mockSameOriginLink,
|
||||
});
|
||||
link({ url: 'external.com/test', newTab: true });
|
||||
link({ url: 'external.com/test', newTab: true, urlQuery: { p: 3 } });
|
||||
expect(mockBackLink.mock.calls).toEqual([]);
|
||||
expect(mockDisabledLink.mock.calls).toEqual([]);
|
||||
expect(mockNoLink.mock.calls).toEqual([]);
|
||||
expect(mockNewOriginLink.mock.calls).toEqual([
|
||||
[
|
||||
{
|
||||
href: 'https://external.com/test',
|
||||
url: 'external.com/test',
|
||||
newTab: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
href: 'https://external.com/test?p=3',
|
||||
url: 'external.com/test',
|
||||
urlQuery: {
|
||||
p: 3,
|
||||
},
|
||||
newTab: true,
|
||||
},
|
||||
],
|
||||
]);
|
||||
expect(mockSameOriginLink.mock.calls).toEqual([]);
|
||||
});
|
||||
|
||||
test('createLink, link with home, not configured', () => {
|
||||
const lowdefy = { inputs: {}, home: { pageId: 'home', configured: false } };
|
||||
const link = createLink({
|
||||
backLink: mockBackLink,
|
||||
disabledLink: mockDisabledLink,
|
||||
lowdefy,
|
||||
newOriginLink: mockNewOriginLink,
|
||||
noLink: mockNoLink,
|
||||
sameOriginLink: mockSameOriginLink,
|
||||
});
|
||||
link({ home: true });
|
||||
link({ home: true, urlQuery: { p: 3 } });
|
||||
expect(mockBackLink.mock.calls).toEqual([]);
|
||||
expect(mockDisabledLink.mock.calls).toEqual([]);
|
||||
expect(mockNoLink.mock.calls).toEqual([]);
|
||||
expect(mockNewOriginLink.mock.calls).toEqual([]);
|
||||
expect(mockSameOriginLink.mock.calls).toEqual([
|
||||
['/home', undefined],
|
||||
['/home?p=3', undefined],
|
||||
[
|
||||
{
|
||||
home: true,
|
||||
href: '/home',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
home: true,
|
||||
href: '/home?p=3',
|
||||
urlQuery: {
|
||||
p: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
test('createLink, link with home new tab', () => {
|
||||
const lowdefy = { inputs: {}, homePageId: 'home' };
|
||||
test('createLink, link with home, configured', () => {
|
||||
const lowdefy = { inputs: {}, home: { pageId: 'home', configured: true } };
|
||||
const link = createLink({
|
||||
backLink: mockBackLink,
|
||||
disabledLink: mockDisabledLink,
|
||||
lowdefy,
|
||||
newOriginLink: mockNewOriginLink,
|
||||
noLink: mockNoLink,
|
||||
sameOriginLink: mockSameOriginLink,
|
||||
});
|
||||
link({ home: true });
|
||||
link({ home: true, urlQuery: { p: 3 } });
|
||||
expect(mockBackLink.mock.calls).toEqual([]);
|
||||
expect(mockDisabledLink.mock.calls).toEqual([]);
|
||||
expect(mockNoLink.mock.calls).toEqual([]);
|
||||
expect(mockNewOriginLink.mock.calls).toEqual([]);
|
||||
expect(mockSameOriginLink.mock.calls).toEqual([
|
||||
[
|
||||
{
|
||||
home: true,
|
||||
href: '/',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
home: true,
|
||||
href: '/?p=3',
|
||||
urlQuery: {
|
||||
p: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
test('createLink, link with home new tab, not configured', () => {
|
||||
const lowdefy = { inputs: {}, home: { pageId: 'home' } };
|
||||
const link = createLink({
|
||||
backLink: mockBackLink,
|
||||
disabledLink: mockDisabledLink,
|
||||
lowdefy,
|
||||
newOriginLink: mockNewOriginLink,
|
||||
noLink: mockNoLink,
|
||||
sameOriginLink: mockSameOriginLink,
|
||||
});
|
||||
link({ home: true, newTab: true });
|
||||
link({ home: true, newTab: true, urlQuery: { p: 3 } });
|
||||
expect(mockBackLink.mock.calls).toEqual([]);
|
||||
expect(mockDisabledLink.mock.calls).toEqual([]);
|
||||
expect(mockNoLink.mock.calls).toEqual([]);
|
||||
expect(mockNewOriginLink.mock.calls).toEqual([]);
|
||||
expect(mockSameOriginLink.mock.calls).toEqual([
|
||||
['/home', true],
|
||||
['/home?p=3', true],
|
||||
[{ home: true, href: '/home', newTab: true }],
|
||||
[
|
||||
{
|
||||
home: true,
|
||||
href: '/home?p=3',
|
||||
newTab: true,
|
||||
urlQuery: {
|
||||
p: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
test('createLink, link with home with inputs', () => {
|
||||
const lowdefy = { inputs: {}, homePageId: 'home' };
|
||||
test('createLink, link with home with inputs, not configured', () => {
|
||||
const lowdefy = { inputs: {}, home: { pageId: 'home' } };
|
||||
const link = createLink({
|
||||
backLink: mockBackLink,
|
||||
disabledLink: mockDisabledLink,
|
||||
lowdefy,
|
||||
newOriginLink: mockNewOriginLink,
|
||||
noLink: mockNoLink,
|
||||
sameOriginLink: mockSameOriginLink,
|
||||
});
|
||||
link({ home: true, input: { a: 1 } });
|
||||
expect(mockBackLink.mock.calls).toEqual([]);
|
||||
expect(mockDisabledLink.mock.calls).toEqual([]);
|
||||
expect(mockNoLink.mock.calls).toEqual([]);
|
||||
expect(mockNewOriginLink.mock.calls).toEqual([]);
|
||||
expect(mockSameOriginLink.mock.calls).toEqual([['/home', undefined]]);
|
||||
expect(mockSameOriginLink.mock.calls).toEqual([
|
||||
[
|
||||
{
|
||||
home: true,
|
||||
href: '/home',
|
||||
input: {
|
||||
a: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
]);
|
||||
expect(lowdefy.inputs).toEqual({
|
||||
home: { a: 1 },
|
||||
'page:home': { a: 1 },
|
||||
});
|
||||
});
|
||||
|
||||
test('createLink, link to throw if no params', () => {
|
||||
const lowdefy = { inputs: {}, homePageId: 'home' };
|
||||
test('createLink, no params calls noLink', () => {
|
||||
const lowdefy = { inputs: {}, home: { pageId: 'home' } };
|
||||
const link = createLink({
|
||||
backLink: mockBackLink,
|
||||
disabledLink: mockDisabledLink,
|
||||
lowdefy,
|
||||
newOriginLink: mockNewOriginLink,
|
||||
noLink: mockNoLink,
|
||||
sameOriginLink: mockSameOriginLink,
|
||||
});
|
||||
expect(() => link({})).toThrowErrorMatchingInlineSnapshot(`"Invalid Link."`);
|
||||
link({});
|
||||
expect(mockBackLink.mock.calls).toEqual([]);
|
||||
expect(mockDisabledLink.mock.calls).toEqual([]);
|
||||
expect(mockNoLink.mock.calls).toEqual([[{}]]);
|
||||
expect(mockNewOriginLink.mock.calls).toEqual([]);
|
||||
expect(mockSameOriginLink.mock.calls).toEqual([]);
|
||||
});
|
||||
|
||||
test('createLink, disabled calls disabledLink', () => {
|
||||
const lowdefy = { inputs: {}, home: { pageId: 'home' } };
|
||||
const link = createLink({
|
||||
backLink: mockBackLink,
|
||||
disabledLink: mockDisabledLink,
|
||||
lowdefy,
|
||||
newOriginLink: mockNewOriginLink,
|
||||
noLink: mockNoLink,
|
||||
sameOriginLink: mockSameOriginLink,
|
||||
});
|
||||
link({ disabled: true, home: true });
|
||||
expect(mockBackLink.mock.calls).toEqual([]);
|
||||
expect(mockDisabledLink.mock.calls).toEqual([
|
||||
[
|
||||
{
|
||||
disabled: true,
|
||||
home: true,
|
||||
},
|
||||
],
|
||||
]);
|
||||
expect(mockNoLink.mock.calls).toEqual([]);
|
||||
expect(mockNewOriginLink.mock.calls).toEqual([]);
|
||||
expect(mockSameOriginLink.mock.calls).toEqual([]);
|
||||
});
|
||||
|
||||
test('createLink, link with back', () => {
|
||||
const lowdefy = { inputs: {} };
|
||||
const link = createLink({
|
||||
backLink: mockBackLink,
|
||||
disabledLink: mockDisabledLink,
|
||||
lowdefy,
|
||||
newOriginLink: mockNewOriginLink,
|
||||
noLink: mockNoLink,
|
||||
sameOriginLink: mockSameOriginLink,
|
||||
});
|
||||
link({ back: true });
|
||||
expect(mockBackLink.mock.calls).toEqual([[]]);
|
||||
expect(mockBackLink.mock.calls).toEqual([
|
||||
[
|
||||
{
|
||||
back: true,
|
||||
},
|
||||
],
|
||||
]);
|
||||
expect(mockDisabledLink.mock.calls).toEqual([]);
|
||||
expect(mockNoLink.mock.calls).toEqual([]);
|
||||
expect(mockNewOriginLink.mock.calls).toEqual([]);
|
||||
expect(mockSameOriginLink.mock.calls).toEqual([]);
|
||||
});
|
||||
|
||||
test('createLink, link with back equal to false is invalid', () => {
|
||||
test('createLink, link with more than one parameter is invalid.', () => {
|
||||
const lowdefy = { inputs: {} };
|
||||
const link = createLink({
|
||||
backLink: mockBackLink,
|
||||
disabledLink: mockDisabledLink,
|
||||
lowdefy,
|
||||
newOriginLink: mockNewOriginLink,
|
||||
noLink: mockNoLink,
|
||||
sameOriginLink: mockSameOriginLink,
|
||||
});
|
||||
expect(() => link({ back: false })).toThrowErrorMatchingInlineSnapshot(`"Invalid Link."`);
|
||||
expect(() => link({ back: true, home: true })).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Invalid Link: To avoid ambiguity, only one of 'back', 'home', 'pageId' or 'url' can be defined."`
|
||||
);
|
||||
});
|
||||
|
@ -19,26 +19,7 @@ import { type, get } from '@lowdefy/helpers';
|
||||
import { Breadcrumb } from 'antd';
|
||||
import { blockDefaultProps } from '@lowdefy/block-utils';
|
||||
|
||||
const ItemLink = ({ basePath, children, className, link, Link }) => {
|
||||
if (type.isString(link.pageId)) {
|
||||
return (
|
||||
<Link href={`${basePath}/${link.pageId}`} className={className}>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
if (type.isString(link.url)) {
|
||||
return (
|
||||
<Link href={link.url} className={className}>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
return <span className={className}>{children}</span>;
|
||||
};
|
||||
|
||||
const BreadcrumbBlock = ({
|
||||
basePath,
|
||||
blockId,
|
||||
events,
|
||||
components: { Icon, Link },
|
||||
@ -61,30 +42,33 @@ const BreadcrumbBlock = ({
|
||||
(() => methods.triggerEvent({ name: onClickActionName, event: { link, index } }))
|
||||
}
|
||||
>
|
||||
<ItemLink
|
||||
basePath={basePath}
|
||||
<Link
|
||||
id={`${blockId}_${index}`}
|
||||
className={methods.makeCssClass([
|
||||
{
|
||||
cursor: events[onClickActionName] && 'pointer',
|
||||
},
|
||||
link.style,
|
||||
])}
|
||||
link={link}
|
||||
Link={Link}
|
||||
{...link}
|
||||
>
|
||||
{link.icon && (
|
||||
<Icon
|
||||
blockId={`${blockId}_${index}_icon`}
|
||||
events={events}
|
||||
properties={{
|
||||
name: type.isString(link.icon) && link.icon,
|
||||
...(type.isObject(link.icon) ? link.icon : {}),
|
||||
style: { paddingRight: 8, ...(link.icon.style || {}) },
|
||||
}}
|
||||
/>
|
||||
{(defaultTitle) => (
|
||||
<>
|
||||
{link.icon && (
|
||||
<Icon
|
||||
blockId={`${blockId}_${index}_icon`}
|
||||
events={events}
|
||||
properties={{
|
||||
name: type.isString(link.icon) && link.icon,
|
||||
...(type.isObject(link.icon) ? link.icon : {}),
|
||||
style: { paddingRight: 8, ...(link.icon.style || {}) },
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{type.isString(link) ? link : link.label || defaultTitle}
|
||||
</>
|
||||
)}
|
||||
{type.isString(link) ? link : link.label || link.pageId || link.url || `Link ${index}`}
|
||||
</ItemLink>
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
))}
|
||||
</Breadcrumb>
|
||||
|
@ -28,26 +28,8 @@ const getDefaultMenu = (menus, menuId = 'default', links) => {
|
||||
return menu.links || [];
|
||||
};
|
||||
|
||||
const getTitle = (id, properties, defaultTitle) =>
|
||||
(properties && properties.title) || defaultTitle || id;
|
||||
|
||||
const MenuTitle = ({ basePath, id, Link, linkStyle, makeCssClass, pageId, properties, url }) => {
|
||||
if (type.isString(pageId)) {
|
||||
return (
|
||||
<Link href={`${basePath}/${pageId}`} className={makeCssClass([linkStyle])}>
|
||||
{getTitle(id, properties, pageId)}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
if (url) {
|
||||
return (
|
||||
<Link href={url} className={makeCssClass([linkStyle])}>
|
||||
{getTitle(id, properties, url)}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
return <span className={makeCssClass([linkStyle])}>{getTitle(id, properties)}</span>;
|
||||
};
|
||||
const getTitle = ({ id, properties, pageId, url }) =>
|
||||
(properties && properties.title) || pageId || url || id;
|
||||
|
||||
const getNestedColors = (menuColor, background) => {
|
||||
const fontColor = color(menuColor, 6);
|
||||
@ -69,7 +51,6 @@ const getNestedColors = (menuColor, background) => {
|
||||
};
|
||||
|
||||
const MenuComp = ({
|
||||
basePath,
|
||||
blockId,
|
||||
components: { Icon, Link },
|
||||
events,
|
||||
@ -218,14 +199,13 @@ const MenuComp = ({
|
||||
])}
|
||||
key={`${link.pageId || link.id}_${i}`}
|
||||
title={
|
||||
<MenuTitle
|
||||
basePath={basePath}
|
||||
Link={Link}
|
||||
linkStyle={methods.makeCssClass(link.style, true)}
|
||||
id={link.id}
|
||||
makeCssClass={methods.makeCssClass}
|
||||
properties={link.properties}
|
||||
/>
|
||||
<Link
|
||||
id={`${link.pageId || link.id}_${i}`}
|
||||
className={methods.makeCssClass(link.style, true)}
|
||||
{...link}
|
||||
>
|
||||
{getTitle(link)}
|
||||
</Link>
|
||||
}
|
||||
icon={
|
||||
link.properties &&
|
||||
@ -253,14 +233,13 @@ const MenuComp = ({
|
||||
<Menu.ItemGroup
|
||||
key={`${subLink.pageId || subLink.id}_${j}`}
|
||||
title={
|
||||
<MenuTitle
|
||||
basePath={basePath}
|
||||
Link={Link}
|
||||
linkStyle={methods.makeCssClass(subLink.style, true)}
|
||||
id={subLink.id}
|
||||
makeCssClass={methods.makeCssClass}
|
||||
properties={subLink.properties}
|
||||
/>
|
||||
<Link
|
||||
id={`${subLink.pageId || subLink.id}_${j}`}
|
||||
className={methods.makeCssClass(subLink.style, true)}
|
||||
{...subLink}
|
||||
>
|
||||
{getTitle(subLink)}
|
||||
</Link>
|
||||
}
|
||||
>
|
||||
{subLink.links.map((subLinkGroup, k) => {
|
||||
@ -288,16 +267,13 @@ const MenuComp = ({
|
||||
)
|
||||
}
|
||||
>
|
||||
<MenuTitle
|
||||
basePath={basePath}
|
||||
Link={Link}
|
||||
linkStyle={methods.makeCssClass(subLinkGroup.style, true)}
|
||||
id={subLinkGroup.id}
|
||||
makeCssClass={methods.makeCssClass}
|
||||
pageId={subLinkGroup.pageId}
|
||||
properties={subLinkGroup.properties}
|
||||
url={subLinkGroup.url}
|
||||
/>
|
||||
<Link
|
||||
id={`${subLinkGroup.pageId || subLinkGroup.id}_${k}`}
|
||||
className={methods.makeCssClass(subLinkGroup.style, true)}
|
||||
{...subLinkGroup}
|
||||
>
|
||||
{getTitle(subLinkGroup)}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
})}
|
||||
@ -320,16 +296,13 @@ const MenuComp = ({
|
||||
)
|
||||
}
|
||||
>
|
||||
<MenuTitle
|
||||
basePath={basePath}
|
||||
Link={Link}
|
||||
linkStyle={methods.makeCssClass(subLink.style, true)}
|
||||
id={subLink.id}
|
||||
makeCssClass={methods.makeCssClass}
|
||||
pageId={subLink.pageId}
|
||||
properties={subLink.properties}
|
||||
url={subLink.url}
|
||||
/>
|
||||
<Link
|
||||
id={`${subLink.pageId || subLink.id}_${j}`}
|
||||
className={methods.makeCssClass(subLink.style, true)}
|
||||
{...subLink}
|
||||
>
|
||||
{getTitle(subLink)}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
}
|
||||
@ -353,16 +326,13 @@ const MenuComp = ({
|
||||
)
|
||||
}
|
||||
>
|
||||
<MenuTitle
|
||||
basePath={basePath}
|
||||
Link={Link}
|
||||
linkStyle={methods.makeCssClass(link.style, true)}
|
||||
id={link.id}
|
||||
makeCssClass={methods.makeCssClass}
|
||||
pageId={link.pageId}
|
||||
properties={link.properties}
|
||||
url={link.url}
|
||||
/>
|
||||
<Link
|
||||
id={`${link.pageId || link.id}_${i}`}
|
||||
className={methods.makeCssClass(link.style, true)}
|
||||
{...link}
|
||||
>
|
||||
{getTitle(link)}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ const PageHeaderMenu = ({
|
||||
components: { Icon, Link },
|
||||
content,
|
||||
events,
|
||||
homePageId,
|
||||
menus,
|
||||
methods,
|
||||
pageId,
|
||||
@ -116,10 +115,9 @@ const PageHeaderMenu = ({
|
||||
])}
|
||||
content={{
|
||||
// TODO: use next/image
|
||||
// TODO: Link to home=true
|
||||
content: () => (
|
||||
<>
|
||||
<Link href={`${homePageId}`}>
|
||||
<Link home={true}>
|
||||
<img
|
||||
src={
|
||||
(properties.logo && properties.logo.src) ||
|
||||
|
@ -35,7 +35,6 @@ const PageSiderMenu = ({
|
||||
components: { Icon, Link },
|
||||
events,
|
||||
content,
|
||||
homePageId,
|
||||
menus,
|
||||
methods,
|
||||
pageId,
|
||||
@ -117,7 +116,6 @@ const PageSiderMenu = ({
|
||||
properties={{ style: mergeObjects([{ minHeight: '100vh' }, properties.style]) }}
|
||||
content={{
|
||||
// TODO: use next/image
|
||||
// TODO: Link to home=true
|
||||
content: () => (
|
||||
<>
|
||||
<Header
|
||||
@ -167,7 +165,7 @@ const PageSiderMenu = ({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Link href={`${basePath}/${homePageId}`}>
|
||||
<Link home={true}>
|
||||
<img
|
||||
src={
|
||||
(properties.logo && properties.logo.src) ||
|
||||
|
@ -15,28 +15,9 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { get, type } from '@lowdefy/helpers';
|
||||
import { get } from '@lowdefy/helpers';
|
||||
import { blockDefaultProps } from '@lowdefy/block-utils';
|
||||
|
||||
const Strong = ({ children, strong }) => (strong ? <b>{children}</b> : <>{children}</>);
|
||||
const Tag = ({ blockId, children, className, disabled, href, Link, newTab, onClick, rel }) =>
|
||||
disabled ? (
|
||||
<span id={blockId} className={className}>
|
||||
{children}
|
||||
</span>
|
||||
) : (
|
||||
<Link
|
||||
id={blockId}
|
||||
className={className}
|
||||
href={href}
|
||||
onClick={onClick}
|
||||
rel={rel || 'noopener noreferrer'}
|
||||
target={newTab ? '_blank' : '_self'}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
|
||||
const AnchorBlock = ({
|
||||
blockId,
|
||||
events,
|
||||
@ -45,38 +26,33 @@ const AnchorBlock = ({
|
||||
methods,
|
||||
properties,
|
||||
}) => {
|
||||
const title = type.isNone(properties.title)
|
||||
? type.isNone(properties.href)
|
||||
? properties.href
|
||||
: blockId
|
||||
: properties.title;
|
||||
const showLoading = get(events, 'onClick.loading') || loading;
|
||||
const disabled = properties.disabled || showLoading;
|
||||
return (
|
||||
<Tag
|
||||
blockId={blockId}
|
||||
<Link
|
||||
id={blockId}
|
||||
className={methods.makeCssClass([
|
||||
properties.style,
|
||||
disabled && { color: '#BEBEBE', cursor: 'not-allowed' },
|
||||
])}
|
||||
disabled={disabled}
|
||||
href={properties.href}
|
||||
Link={Link}
|
||||
rel={properties.rel}
|
||||
newTab={properties.newTab}
|
||||
onClick={() => methods.triggerEvent({ name: 'onClick' })}
|
||||
{...properties}
|
||||
>
|
||||
<Strong strong={properties.strong}>
|
||||
{properties.icon && (
|
||||
<Icon
|
||||
blockId={`${blockId}_icon`}
|
||||
events={events}
|
||||
properties={showLoading ? { name: 'LoadingOutlined', spin: true } : properties.icon}
|
||||
/>
|
||||
)}
|
||||
{` ${title}`}
|
||||
</Strong>
|
||||
</Tag>
|
||||
{(defaultTitle) => (
|
||||
<>
|
||||
{properties.icon &&
|
||||
(
|
||||
<Icon
|
||||
blockId={`${blockId}_icon`}
|
||||
events={events}
|
||||
properties={showLoading ? { name: 'LoadingOutlined', spin: true } : properties.icon}
|
||||
/>
|
||||
) + ` `}
|
||||
{properties.title || defaultTitle}
|
||||
</>
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -7,6 +7,7 @@ module.exports = withLess({
|
||||
modifyVars: lowdefyConfig.theme.lessVariables,
|
||||
},
|
||||
},
|
||||
basePath: process.env.LOWDEFY_BASE_PATH || lowdefyConfig.basePath,
|
||||
// reactStrictMode: true,
|
||||
webpack: (config, { isServer }) => {
|
||||
if (!isServer) {
|
||||
|
@ -1,7 +1,9 @@
|
||||
const withLess = require('next-with-less');
|
||||
const lowdefyConfig = require('./build/config.json');
|
||||
|
||||
// TODO: Trance env and args from cli that is required on the server.
|
||||
module.exports = withLess({
|
||||
basePath: process.env.LOWDEFY_BASE_PATH || lowdefyConfig.basePath,
|
||||
lessLoaderOptions: {
|
||||
lessOptions: {
|
||||
modifyVars: lowdefyConfig.theme.lessVariables,
|
||||
|
@ -19,14 +19,13 @@ import React from 'react';
|
||||
import callRequest from '../utils/callRequest.js';
|
||||
import blockComponents from '../../build/plugins/blocks.js';
|
||||
import operators from '../../build/plugins/operatorsClient.js';
|
||||
import components from './components.js';
|
||||
|
||||
const LowdefyContext = ({ children }) => {
|
||||
const lowdefy = {
|
||||
_internal: {
|
||||
const LowdefyContext = ({ children, lowdefy }) => {
|
||||
if (!lowdefy._internal) {
|
||||
lowdefy._internal = {
|
||||
blockComponents,
|
||||
callRequest,
|
||||
components,
|
||||
components: {},
|
||||
document,
|
||||
operators,
|
||||
updaters: {},
|
||||
@ -36,14 +35,14 @@ const LowdefyContext = ({ children }) => {
|
||||
return () => undefined;
|
||||
},
|
||||
link: () => undefined,
|
||||
},
|
||||
contexts: {},
|
||||
inputs: {},
|
||||
lowdefyGlobal: {},
|
||||
};
|
||||
};
|
||||
lowdefy.contexts = {};
|
||||
lowdefy.inputs = {};
|
||||
lowdefy.lowdefyGlobal = {};
|
||||
}
|
||||
lowdefy._internal.updateBlock = (blockId) =>
|
||||
lowdefy._internal.updaters[blockId] && lowdefy._internal.updaters[blockId]();
|
||||
return <>{children(lowdefy)}</>;
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export default LowdefyContext;
|
||||
|
@ -16,25 +16,29 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { urlQuery } from '@lowdefy/helpers';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import Context from './Context.js';
|
||||
import Head from './Head.js';
|
||||
import Block from './block/Block.js';
|
||||
import setupLink from '../utils/setupLink.js';
|
||||
import createComponents from './createComponents.js';
|
||||
|
||||
const LoadingBlock = () => <div>Loading...</div>;
|
||||
|
||||
const Page = ({ lowdefy, pageConfig, rootConfig }) => {
|
||||
const router = useRouter();
|
||||
lowdefy._internal.basePath = router.basePath;
|
||||
lowdefy._internal.pathname = router.pathname;
|
||||
lowdefy._internal.query = router.query;
|
||||
lowdefy._internal.router = router;
|
||||
lowdefy._internal.link = setupLink({ lowdefy });
|
||||
lowdefy._internal.link = setupLink(lowdefy);
|
||||
lowdefy._internal.components = createComponents(lowdefy);
|
||||
|
||||
lowdefy.basePath = lowdefy._internal.router.basePath;
|
||||
lowdefy.home = rootConfig.home;
|
||||
lowdefy.lowdefyGlobal = rootConfig.lowdefyGlobal;
|
||||
lowdefy.menus = rootConfig.menus;
|
||||
lowdefy.urlQuery = urlQuery.parse(window.location.search.slice(1));
|
||||
|
||||
return (
|
||||
<Context config={pageConfig} lowdefy={lowdefy}>
|
||||
{(context, loading) => {
|
||||
|
@ -64,12 +64,10 @@ const CategorySwitch = ({ block, Blocks, context, lowdefy }) => {
|
||||
setValue: block.setValue,
|
||||
triggerEvent: block.triggerEvent,
|
||||
})}
|
||||
// TODO: React throws a basePath warning
|
||||
basePath={lowdefy._internal.basePath}
|
||||
basePath={lowdefy.basePath}
|
||||
blockId={block.blockId}
|
||||
components={lowdefy._internal.components}
|
||||
events={block.eval.events}
|
||||
homePageId={lowdefy.home.pageId}
|
||||
key={block.blockId}
|
||||
loading={block.loading}
|
||||
menus={lowdefy.menus}
|
||||
@ -98,11 +96,10 @@ const CategorySwitch = ({ block, Blocks, context, lowdefy }) => {
|
||||
registerMethod: block.registerMethod,
|
||||
triggerEvent: block.triggerEvent,
|
||||
})}
|
||||
basePath={lowdefy._internal.basePath}
|
||||
basePath={lowdefy.basePath}
|
||||
blockId={block.blockId}
|
||||
components={lowdefy._internal.components}
|
||||
events={block.eval.events}
|
||||
homePageId={lowdefy.home.pageId}
|
||||
key={block.blockId}
|
||||
loading={block.loading}
|
||||
menus={lowdefy.menus}
|
||||
|
@ -65,12 +65,11 @@ const Container = ({ block, Blocks, Component, context, lowdefy }) => {
|
||||
registerMethod: block.registerMethod,
|
||||
triggerEvent: block.triggerEvent,
|
||||
})}
|
||||
basePath={lowdefy._internal.basePath}
|
||||
basePath={lowdefy.basePath}
|
||||
blockId={block.blockId}
|
||||
components={lowdefy._internal.components}
|
||||
content={content}
|
||||
events={block.eval.events}
|
||||
homePageId={lowdefy.home.pageId}
|
||||
key={block.blockId}
|
||||
loading={block.loading}
|
||||
menus={lowdefy.menus}
|
||||
|
@ -72,11 +72,10 @@ const List = ({ block, Blocks, Component, context, lowdefy }) => {
|
||||
triggerEvent: block.triggerEvent,
|
||||
unshiftItem: block.unshiftItem,
|
||||
})}
|
||||
basePath={lowdefy._internal.basePath}
|
||||
basePath={lowdefy.basePath}
|
||||
blockId={block.blockId}
|
||||
components={lowdefy._internal.components}
|
||||
events={block.eval.events}
|
||||
homePageId={lowdefy.home.pageId}
|
||||
key={block.blockId}
|
||||
list={contentList}
|
||||
loading={block.loading}
|
||||
|
@ -14,12 +14,16 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Link from 'next/link';
|
||||
import { createIcon } from '@lowdefy/block-utils';
|
||||
|
||||
import createLinkComponent from './createLinkComponent.js';
|
||||
import icons from '../../build/plugins/icons.js';
|
||||
|
||||
export default {
|
||||
Link,
|
||||
Icon: createIcon(icons),
|
||||
const createComponents = (lowdefy) => {
|
||||
return {
|
||||
Link: createLinkComponent(lowdefy),
|
||||
Icon: createIcon(icons),
|
||||
};
|
||||
};
|
||||
|
||||
export default createComponents;
|
92
packages/server/src/components/createLinkComponent.js
Normal file
92
packages/server/src/components/createLinkComponent.js
Normal file
@ -0,0 +1,92 @@
|
||||
import React from 'react';
|
||||
import NextLink from 'next/link';
|
||||
import { createLink } from '@lowdefy/engine';
|
||||
import { type } from '@lowdefy/helpers';
|
||||
|
||||
const createLinkComponent = (lowdefy) => {
|
||||
const backLink = ({ ariaLabel, children, className, id, rel }) => (
|
||||
<a
|
||||
id={id}
|
||||
onClick={() => lowdefy._internal.router.back()}
|
||||
className={className}
|
||||
rel={rel}
|
||||
aria-label={ariaLabel || 'back'}
|
||||
>
|
||||
{type.isFunction(children) ? children(id) : children}
|
||||
</a>
|
||||
);
|
||||
const newOriginLink = ({
|
||||
ariaLabel,
|
||||
children,
|
||||
className,
|
||||
href,
|
||||
id,
|
||||
newTab,
|
||||
pageId,
|
||||
rel,
|
||||
url,
|
||||
}) => {
|
||||
return (
|
||||
<a
|
||||
id={id}
|
||||
aria-label={ariaLabel}
|
||||
className={className}
|
||||
href={href}
|
||||
rel={rel || (newTab && 'noopener noreferrer')}
|
||||
target={newTab && '_blank'}
|
||||
>
|
||||
{type.isFunction(children) ? children(pageId || url || id) : children}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
const sameOriginLink = ({
|
||||
ariaLabel,
|
||||
children,
|
||||
className,
|
||||
href,
|
||||
id,
|
||||
newTab,
|
||||
pageId,
|
||||
rel,
|
||||
replace,
|
||||
scroll,
|
||||
url,
|
||||
}) => {
|
||||
if (newTab) {
|
||||
return (
|
||||
<a
|
||||
id={id}
|
||||
aria-label={ariaLabel}
|
||||
className={className}
|
||||
href={`${window.location.origin}${lowdefy.basePath}${href}`}
|
||||
rel={rel || 'noopener noreferrer'}
|
||||
target="_blank"
|
||||
>
|
||||
{type.isFunction(children) ? children(pageId || url || id) : children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<NextLink href={href} replace={replace} scroll={scroll}>
|
||||
<a id={id} aria-label={ariaLabel} className={className} rel={rel}>
|
||||
{type.isFunction(children) ? children(pageId || url || id) : children}
|
||||
</a>
|
||||
</NextLink>
|
||||
);
|
||||
};
|
||||
const noLink = ({ className, children, id }) => (
|
||||
<span id={id} className={className}>
|
||||
{type.isFunction(children) ? children(id) : children}
|
||||
</span>
|
||||
);
|
||||
return createLink({
|
||||
backLink,
|
||||
lowdefy,
|
||||
newOriginLink,
|
||||
sameOriginLink,
|
||||
noLink,
|
||||
disabledLink: noLink,
|
||||
});
|
||||
};
|
||||
|
||||
export default createLinkComponent;
|
@ -22,12 +22,14 @@ import LowdefyContext from '../components/LowdefyContext.js';
|
||||
|
||||
import '../../build/plugins/styles.less';
|
||||
|
||||
const lowdefy = {};
|
||||
|
||||
function App({ Component, pageProps }) {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<Suspense>
|
||||
<LowdefyContext>
|
||||
{(lowdefy) => <Component lowdefy={lowdefy} {...pageProps} />}
|
||||
<LowdefyContext lowdefy={lowdefy}>
|
||||
<Component lowdefy={lowdefy} {...pageProps} />
|
||||
</LowdefyContext>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
|
@ -19,6 +19,7 @@ import { createApiContext, getPageConfig, getRootConfig } from '@lowdefy/api';
|
||||
import Page from '../components/Page.js';
|
||||
|
||||
export async function getServerSideProps() {
|
||||
// TODO: is this build directory configurable from the cli?
|
||||
const apiContext = await createApiContext({ buildDirectory: './build' });
|
||||
const rootConfig = await getRootConfig(apiContext);
|
||||
const { home } = rootConfig;
|
||||
|
@ -16,29 +16,30 @@
|
||||
|
||||
import { createLink } from '@lowdefy/engine';
|
||||
|
||||
function setupLink({ lowdefy }) {
|
||||
function setupLink(lowdefy) {
|
||||
const { router, window } = lowdefy._internal;
|
||||
const sameOriginLink = (path, newTab) => {
|
||||
const backLink = () => router.back();
|
||||
const disabledLink = () => {};
|
||||
const newOriginLink = ({ href, newTab }) => {
|
||||
if (newTab) {
|
||||
return window.open(`${window.location.origin}${lowdefy.basePath}${path}`, '_blank').focus();
|
||||
return window.open(href, '_blank').focus();
|
||||
} else {
|
||||
return window.location.assign(href);
|
||||
}
|
||||
};
|
||||
const sameOriginLink = ({ href, newTab }) => {
|
||||
if (newTab) {
|
||||
return window.open(`${window.location.origin}${lowdefy.basePath}${href}`, '_blank').focus();
|
||||
} else {
|
||||
// Next handles the basePath here.
|
||||
return router.push({
|
||||
pathname: path,
|
||||
// TODO: Do we handle urlQuery as a param here?
|
||||
// query: {},
|
||||
pathname: href, // href includes the urlQuery as defined by engine
|
||||
});
|
||||
}
|
||||
};
|
||||
const newOriginLink = (path, newTab) => {
|
||||
if (newTab) {
|
||||
return window.open(path, '_blank').focus();
|
||||
} else {
|
||||
return (window.location.href = path);
|
||||
}
|
||||
const noLink = () => {
|
||||
throw new Error(`Invalid Link.`);
|
||||
};
|
||||
const backLink = () => window.history.back();
|
||||
return createLink({ backLink, lowdefy, newOriginLink, sameOriginLink });
|
||||
return createLink({ backLink, disabledLink, lowdefy, newOriginLink, noLink, sameOriginLink });
|
||||
}
|
||||
|
||||
export default setupLink;
|
||||
|
@ -30,7 +30,6 @@ const lowdefyProps = [
|
||||
'components',
|
||||
'content',
|
||||
'eventLog',
|
||||
'homePageId',
|
||||
'list',
|
||||
'loading',
|
||||
'menus',
|
||||
|
@ -20,7 +20,7 @@ function getConfigFromEnv() {
|
||||
logLevel: process.env.LOWDEFY_SERVER_LOG_LEVEL,
|
||||
publicDirectory: process.env.LOWDEFY_SERVER_PUBLIC_DIRECTORY,
|
||||
port: process.env.LOWDEFY_SERVER_PORT && parseInt(process.env.LOWDEFY_SERVER_PORT),
|
||||
serverBasePath: process.env.LOWDEFY_SERVER_BASE_PATH,
|
||||
basePath: process.env.LOWDEFY_BASE_PATH,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ test('Get config from env', () => {
|
||||
buildDirectory: 'build',
|
||||
publicDirectory: 'public',
|
||||
port: 8080,
|
||||
serverBasePath: 'base',
|
||||
basePath: 'base',
|
||||
});
|
||||
});
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user