Skip to content

Commit

Permalink
fix: sync path.value (QwikDev#2668)
Browse files Browse the repository at this point in the history
  • Loading branch information
manucorporat authored Jan 18, 2023
1 parent 7ff32c5 commit 71b252f
Show file tree
Hide file tree
Showing 25 changed files with 176 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,12 @@ const _resolveRequestHandlers = (
}

if (collectActions) {
const loaders = Object.values(routeModule).filter(
(e) => e.__brand === 'server_loader'
const loaders = Object.values(routeModule).filter((e) =>
checkBrand(e, 'server_loader')
) as any[];

const actions = Object.values(routeModule).filter(
(e) => e.__brand === 'server_action'
const actions = Object.values(routeModule).filter((e) =>
checkBrand(e, 'server_action')
) as any[];

serverLoaders.push(...loaders);
Expand All @@ -135,6 +135,10 @@ const _resolveRequestHandlers = (
}
};

export const checkBrand = (obj: any, brand: string) => {
return obj && typeof obj === 'object' && obj.__brand === brand;
};

export function actionsMiddleware(
serverLoaders: ServerLoaderInternal[],
serverActions: ServerActionInternal[]
Expand Down
9 changes: 8 additions & 1 deletion packages/qwik-city/runtime/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { CookieOptions } from '@builder.io/qwik-city/middleware/request-handler'
import { CookieValue } from '@builder.io/qwik-city/middleware/request-handler';
import type { GetSyncData } from '@builder.io/qwik-city/middleware/request-handler';
import { JSXNode } from '@builder.io/qwik';
import { PropFunction } from '@builder.io/qwik';
import { QRL } from '@builder.io/qwik';
import { QwikIntrinsicElements } from '@builder.io/qwik';
import { QwikJSX } from '@builder.io/qwik';
Expand Down Expand Up @@ -149,14 +150,20 @@ export interface DocumentStyle {
export type EndpointHandler<BODY = unknown> = RequestHandler<BODY>;

// @alpha (undocumented)
export const Form: <T>({ action, ...rest }: FormProps<T>) => JSXNode<"form">;
export const Form: <T>({ action, spaReset, reloadDocument, onSubmit$, ...rest }: FormProps<T>) => JSXNode<"form">;

// @alpha (undocumented)
export interface FormProps<T> extends Omit<QwikJSX.IntrinsicElements['form'], 'action'> {
// (undocumented)
action: ServerActionUse<T>;
// (undocumented)
method?: 'post';
// (undocumented)
onSubmit$?: PropFunction<(event: Event) => void>;
// (undocumented)
reloadDocument?: boolean;
// (undocumented)
spaReset?: boolean;
}

// Warning: (ae-forgotten-export) The symbol "QwikCityProps" needs to be exported by the entry point index.d.ts
Expand Down
20 changes: 15 additions & 5 deletions packages/qwik-city/runtime/src/form-component.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
import type { ServerActionUse } from './server-functions';
import { jsx, _wrapSignal, QwikJSX } from '@builder.io/qwik';
import { jsx, _wrapSignal, QwikJSX, PropFunction } from '@builder.io/qwik';

/**
* @alpha
*/
export interface FormProps<T> extends Omit<QwikJSX.IntrinsicElements['form'], 'action'> {
action: ServerActionUse<T>;
method?: 'post';
onSubmit$?: PropFunction<(event: Event) => void>;
reloadDocument?: boolean;
spaReset?: boolean;
}

/**
* @alpha
*/
export const Form = <T,>({ action, ...rest }: FormProps<T>) => {
export const Form = <T,>({
action,
spaReset,
reloadDocument,
onSubmit$,
...rest
}: FormProps<T>) => {
return jsx('form', {
action: action.actionPath,
'preventdefault:submit': true,
onSubmit$: action.run,
...rest,
action: action.actionPath,
'preventdefault:submit': !reloadDocument,
onSubmit$: [!reloadDocument ? action.run : undefined, onSubmit$],
method: 'post',
['data-spa-reset']: spaReset ? 'true' : undefined,
});
};
2 changes: 1 addition & 1 deletion packages/qwik-city/runtime/src/head.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type {
ClientPageData,
} from './types';

export const resolveHead = async (
export const resolveHead = (
endpoint: EndpointResponse | ClientPageData | undefined | null,
routeLocation: RouteLocation,
contentModules: ContentModule[],
Expand Down
11 changes: 5 additions & 6 deletions packages/qwik-city/runtime/src/qwik-city-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,19 +166,18 @@ export const QwikCityProvider = component$<QwikCityProps>(() => {
const [params, mods, menu] = loadedRoute;
const contentModules = mods as ContentModule[];
const pageModule = contentModules[contentModules.length - 1] as PageModule;
const resolvedHead = await resolveHead(
clientPageData,
routeLocation,
contentModules,
locale
);

// Update route location
routeLocation.href = url.href;
routeLocation.pathname = pathname;
routeLocation.params = { ...params };
routeLocation.query = url.searchParams;

(navPath as any).untrackedValue = pathname;

// Needs to be done after routeLocation is updated
const resolvedHead = resolveHead(clientPageData, routeLocation, contentModules, locale);

// Update content
content.headings = pageModule.headings;
content.menu = menu;
Expand Down
6 changes: 5 additions & 1 deletion packages/qwik-city/runtime/src/server-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ export class ServerActionImpl implements ServerActionInternal {
initialState.run = $((input) => {
let data: FormData;
if (input instanceof SubmitEvent) {
data = new FormData(input.target as HTMLFormElement);
const form = input.target as HTMLFormElement;
data = new FormData(form);
if (form.getAttribute('data-spa-reset') === 'true') {
form.reset();
}
} else if (input instanceof FormData) {
data = input;
} else {
Expand Down
8 changes: 4 additions & 4 deletions packages/qwik/src/core/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -454,10 +454,6 @@ export interface QwikFocusEvent<T = Element> extends SyntheticEvent<T, NativeFoc
target: EventTarget & T;
}

// @beta (undocumented)
export interface QwikFormEvent<T = Element> extends SyntheticEvent<T> {
}

// Warning: (ae-forgotten-export) The symbol "IntrinsicHTMLElements" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
Expand Down Expand Up @@ -584,6 +580,10 @@ export interface QwikPointerEvent<T = Element> extends QwikMouseEvent<T, NativeP
width: number;
}

// @beta (undocumented)
export interface QwikSubmitEvent<T = Element> extends SyntheticEvent<T> {
}

// @beta (undocumented)
export interface QwikTouchEvent<T = Element> extends SyntheticEvent<T, NativeTouchEvent> {
// (undocumented)
Expand Down
2 changes: 1 addition & 1 deletion packages/qwik/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export type {
QwikDragEvent,
QwikPointerEvent,
QwikFocusEvent,
QwikFormEvent,
QwikSubmitEvent,
QwikInvalidEvent,
QwikChangeEvent,
QwikKeyboardEvent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
QwikKeyboardEvent,
QwikMouseEvent,
QwikPointerEvent,
QwikSubmitEvent,
QwikTouchEvent,
QwikTransitionEvent,
QwikUIEvent,
Expand Down Expand Up @@ -47,7 +48,7 @@ export type QwikEventMap<T> = {
InputCapture: Event;
Reset: Event;
ResetCapture: Event;
Submit: Event;
Submit: QwikSubmitEvent<T>;
SubmitCapture: Event;
Invalid: QwikInvalidEvent<T>;
InvalidCapture: QwikInvalidEvent<T>;
Expand Down Expand Up @@ -215,7 +216,7 @@ export interface QwikCustomEvents {
| SingleOrArray<undefined>;
}

type SingleOrArray<T> = T | T[];
type SingleOrArray<T> = T | (SingleOrArray<T> | undefined | null)[];

export type QwikKnownEvents<T> = {
[K in keyof QwikEventMap<T> as `${'document:' | 'window:' | ''}on${K}$`]?: SingleOrArray<
Expand Down
2 changes: 1 addition & 1 deletion packages/qwik/src/core/render/jsx/types/jsx-qwik-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export interface QwikFocusEvent<T = Element> extends SyntheticEvent<T, NativeFoc
/**
* @beta
*/
export interface QwikFormEvent<T = Element> extends SyntheticEvent<T> {}
export interface QwikSubmitEvent<T = Element> extends SyntheticEvent<T> {}

/**
* @beta
Expand Down
8 changes: 8 additions & 0 deletions packages/qwik/src/core/render/ssr/render-ssr.unit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,14 @@ renderSSRSuite('events', async () => {
<body onClick$={() => console.warn('hol')}>hola</body>,
'<html q:container="paused" q:version="dev" q:render="ssr-dev"><body on:click="/runtimeQRL#_">hola</body></html>'
);
await testSSR(
<body onClick$={[undefined, $(() => console.warn('hol'))]}>hola</body>,
'<html q:container="paused" q:version="dev" q:render="ssr-dev"><body on:click="/runtimeQRL#_">hola</body></html>'
);
await testSSR(
<body onClick$={[undefined, [$(() => console.warn('hol'))]]}>hola</body>,
'<html q:container="paused" q:version="dev" q:render="ssr-dev"><body on:click="/runtimeQRL#_">hola</body></html>'
);
await testSSR(
<body document:onClick$={() => console.warn('hol')}>hola</body>,
'<html q:container="paused" q:version="dev" q:render="ssr-dev"><body on-document:click="/runtimeQRL#_">hola</body></html>'
Expand Down
6 changes: 5 additions & 1 deletion packages/qwik/src/core/state/listeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ export const setEvent = (
prop = normalizeOnProp(prop.slice(0, -1));
if (input) {
if (isArray(input)) {
existingListeners.push(...input.map((q) => [prop, ensureQrl(q, containerEl)] as Listener));
const processed = input
.flat(Infinity)
.filter((q) => q != null)
.map((q) => [prop, ensureQrl(q, containerEl)] as Listener);
existingListeners.push(...processed);
} else {
existingListeners.push([prop, ensureQrl(input, containerEl)]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const TwoListeners = component$(() => {
href="/"
preventdefault:click
class="two-listeners"
onClick$={[$(() => store1.count++), update, update]}
onClick$={[$(() => store1.count++), update, undefined, [null, update]]}
>
{store1.count} / {store2.count}
</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default component$(() => {
<div>
<h1>Sign In</h1>

<Form action={signIn}>
<Form action={signIn} spaReset>
{signIn.value?.message && <p style="color:red">{signIn.value.message}</p>}
<label>
<span>Username</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const data: string[] = [];
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { component$ } from '@builder.io/qwik';
import { action$, Form } from '@builder.io/qwik-city';
import { data } from './data';

export const rootAction = action$((form, { redirect }) => {
const name = form.get('name');
data.push(name as string);
throw redirect(303, '/qwikcity-test/issue2644/other/');
});

export default component$(() => {
const action = rootAction.use();
return (
<Form action={action}>
<input name="name" id="issue2644-input" />
<button id="issue2644-submit">Submit</button>
</Form>
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { component$ } from '@builder.io/qwik';
import { action$, loader$, Form } from '@builder.io/qwik-city';
import { data } from '../data';

export const getData = loader$(() => {
return data;
});

export const otherAction = action$((form) => {
const name = form.get('name');
data.push(name as string);
return { success: true };
});

export default component$(() => {
const items = getData.use();
const action = otherAction.use();
return (
<div>
<ul id="issue2644-list">
{items.value.map((x) => (
<li>{x}</li>
))}
</ul>
<Form action={action}>
<input name="name" id="issue2644-input" />
<button id="issue2644-submit">Submit</button>
</Form>
</div>
);
});
2 changes: 2 additions & 0 deletions starters/apps/qwikcity-test/src/routes/(common)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { component$, Slot } from '@builder.io/qwik';
import Footer from '../../components/footer/footer';
import Header from '../../components/header/header';

export const isNull = null;

export default component$(() => {
return (
<div data-test-layout="root">
Expand Down
34 changes: 34 additions & 0 deletions starters/e2e/qwikcity/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { expect, test } from '@playwright/test';

test.describe('loaders', () => {
test.describe('mpa', () => {
test.use({ javaScriptEnabled: false });
tests();
});

test.describe('spa', () => {
test.use({ javaScriptEnabled: true });
tests();
});

function tests() {
test.describe('issue2644', () => {
test('should submit items', async ({ page }) => {
await page.goto('/qwikcity-test/issue2644/');
await page.locator('#issue2644-input').fill('AAA');
await page.locator('#issue2644-submit').click();
await page.waitForTimeout(200);

const pageUrl = new URL(page.url());
await expect(pageUrl.pathname).toBe('/qwikcity-test/issue2644/other/');
await expect(page.locator('#issue2644-list > li')).toHaveText(['AAA']);

await page.locator('#issue2644-input').fill('BBB');
await page.locator('#issue2644-submit').click();
await expect(pageUrl.pathname).toBe('/qwikcity-test/issue2644/other/');

await expect(page.locator('#issue2644-list > li')).toHaveText(['AAA', 'BBB']);
});
});
}
});
14 changes: 6 additions & 8 deletions starters/e2e/qwikcity/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ test.describe('Qwik City Auth', () => {
tests();
});

// test.describe('spa', () => {
// test.use({javaScriptEnabled: true});
// tests();
// });
test.describe('spa', () => {
test.use({ javaScriptEnabled: true });
tests();
});
});

function tests() {
Expand All @@ -34,8 +34,7 @@ function tests() {
await page.keyboard.type('dev');

/*********** Unsuccessful Sign In ***********/
const [try1] = await Promise.all([page.waitForNavigation(), page.click('[data-test-sign-in]')]);
expect(try1!.status()).toBe(403);
await linkNavigate(ctx, '[data-test-sign-in]', 403);

page = getPage(ctx);
await page.focus('input[name="username"]');
Expand All @@ -45,8 +44,7 @@ function tests() {
await page.keyboard.type('dev');

/*********** Successful Sign In, Dashboard ***********/
const [try2] = await Promise.all([page.waitForNavigation(), page.click('[data-test-sign-in]')]);
expect(try2!.status()).toBe(200);
await linkNavigate(ctx, '[data-test-sign-in]', 200);

await assertPage(ctx, {
pathname: '/qwikcity-test/dashboard/',
Expand Down
4 changes: 4 additions & 0 deletions starters/e2e/qwikcity/catchall.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ test.describe('Qwik City Catchall', () => {
test.use({ javaScriptEnabled: false });
tests();
});
test.describe('spa', () => {
test.use({ javaScriptEnabled: true });
tests();
});
});

function tests() {
Expand Down
Loading

0 comments on commit 71b252f

Please sign in to comment.