fix(UiKit): Modal validation not working (#32679)

pull/32837/head^2
Tiago Evangelista Pinto 1 year ago committed by GitHub
parent 35985d7e18
commit 88e5219bd2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      .changeset/chatty-hounds-hammer.md
  2. 4
      apps/meteor/client/uikit/hooks/useModalContextValue.ts
  3. 2
      apps/meteor/client/views/modal/uikit/UiKitModal.tsx
  4. 8
      apps/meteor/tests/e2e/apps/app-modal-interaction.spec.ts
  5. 20
      apps/meteor/tests/e2e/apps/apps-contextualbar.spec.ts
  6. 49
      apps/meteor/tests/e2e/apps/apps-modal.spec.ts
  7. 2
      apps/meteor/tests/e2e/fixtures/insert-apps.ts
  8. 21
      apps/meteor/tests/e2e/page-objects/modal.ts
  9. 2
      packages/fuselage-ui-kit/src/contexts/UiKitContext.ts
  10. 6
      packages/fuselage-ui-kit/src/hooks/useUiKitState.ts

@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": patch
"@rocket.chat/fuselage-ui-kit": patch
---
Fix validations from "UiKit" modal component

@ -14,6 +14,7 @@ type UseModalContextValueParams = {
blockId?: string | undefined;
};
};
errors?: { [field: string]: string }[] | { [field: string]: string };
updateValues: Dispatch<{
actionId: string;
payload: {
@ -25,7 +26,7 @@ type UseModalContextValueParams = {
type UseModalContextValueReturn = ContextType<typeof UiKitContext>;
export const useModalContextValue = ({ view, values, updateValues }: UseModalContextValueParams): UseModalContextValueReturn => {
export const useModalContextValue = ({ view, errors, values, updateValues }: UseModalContextValueParams): UseModalContextValueReturn => {
const actionManager = useUiKitActionManager();
const emitInteraction = useMemo(() => actionManager.emitInteraction.bind(actionManager), [actionManager]);
@ -62,6 +63,7 @@ export const useModalContextValue = ({ view, values, updateValues }: UseModalCon
});
},
...view,
errors,
values,
viewId: view.id,
};

@ -20,7 +20,7 @@ type UiKitModalProps = {
const UiKitModal = ({ initialView }: UiKitModalProps) => {
const actionManager = useUiKitActionManager();
const { view, errors, values, updateValues, state } = useUiKitView(initialView);
const contextValue = useModalContextValue({ view, values, updateValues });
const contextValue = useModalContextValue({ view, errors, values, updateValues });
const handleSubmit = useEffectEvent((e: FormEvent) => {
preventSyntheticEvent(e);

@ -1,7 +1,7 @@
import { Users } from './fixtures/userStates';
import { HomeChannel } from './page-objects';
import { createTargetChannel } from './utils/create-target-channel';
import { test, expect } from './utils/test';
import { Users } from '../fixtures/userStates';
import { HomeChannel } from '../page-objects';
import { createTargetChannel } from '../utils/create-target-channel';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.admin.state });

@ -1,26 +1,34 @@
import { Users } from './fixtures/userStates';
import { HomeChannel } from './page-objects';
import { expect, test } from './utils/test';
import type { Page } from '@playwright/test';
import { Users } from '../fixtures/userStates';
import { HomeChannel } from '../page-objects';
import { expect, test } from '../utils/test';
test.use({ storageState: Users.user1.state });
test.describe.serial('Apps', () => {
test.describe.serial('Apps > ContextualBar', () => {
let poHomeChannel: HomeChannel;
test.beforeEach(async ({ page }) => {
let page: Page;
test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
poHomeChannel = new HomeChannel(page);
await page.goto('/home');
await poHomeChannel.sidenav.openChat('general');
});
test.afterAll(async () => {
await page.close();
});
test('expect allow user open app contextualbar', async () => {
await poHomeChannel.content.dispatchSlashCommand('/contextualbar');
await expect(poHomeChannel.btnContextualbarClose).toBeVisible();
});
test('expect app contextualbar to be closed', async () => {
await poHomeChannel.content.dispatchSlashCommand('/contextualbar');
await poHomeChannel.btnContextualbarClose.click();
await expect(poHomeChannel.btnContextualbarClose).toBeHidden();
});

@ -0,0 +1,49 @@
import type { Page } from '@playwright/test';
import { Users } from '../fixtures/userStates';
import { HomeChannel } from '../page-objects';
import { Modal } from '../page-objects/modal';
import { expect, test } from '../utils/test';
test.use({ storageState: Users.user1.state });
test.describe.serial('Apps > ContextualBar', () => {
let poHomeChannel: HomeChannel;
let poModal: Modal;
let page: Page;
test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
poHomeChannel = new HomeChannel(page);
poModal = new Modal(page);
await page.goto('/home');
await poHomeChannel.sidenav.openChat('general');
});
test.afterAll(async () => {
await page.close();
});
test('expect allow user open app modal', async () => {
await poHomeChannel.content.dispatchSlashCommand('/modal');
await expect(poModal.btnModalSubmit).toBeVisible();
});
test('expect validation error message appears in app modal', async () => {
await expect(poModal.textInput).toBeVisible();
await poModal.btnModalSubmit.click();
await expect(poModal.textInputErrorMessage).toBeVisible();
});
test("expect validation error message don't appears in app modal", async () => {
await poModal.textInput.fill('something');
await poModal.btnModalSubmit.click();
await expect(poModal.textInputErrorMessage).not.toBeVisible();
});
});

@ -3,7 +3,7 @@ import { request } from '@playwright/test';
import { BASE_API_URL, BASE_URL } from '../config/constants';
import { Users } from './userStates';
const APP_URL = 'https://github.com/RocketChat/Apps.RocketChat.Tester/blob/master/dist/appsrocketchattester_0.0.5.zip?raw=true';
const APP_URL = 'https://github.com/RocketChat/Apps.RocketChat.Tester/blob/master/dist/appsrocketchattester_0.1.0.zip?raw=true';
export default async function insertApp(): Promise<void> {
const api = await request.newContext();

@ -0,0 +1,21 @@
import type { Locator, Page } from '@playwright/test';
export class Modal {
protected readonly page: Page;
constructor(page: Page) {
this.page = page;
}
get textInput(): Locator {
return this.page.locator('[name="modal_input"]');
}
get textInputErrorMessage(): Locator {
return this.page.getByText('Validation failed');
}
get btnModalSubmit(): Locator {
return this.page.locator('role=button[name="Submit"]');
}
}

@ -26,7 +26,7 @@ type UiKitContextValue = {
event: Parameters<React.MouseEventHandler<HTMLElement>>[0]
) => Promise<void> | void;
appId?: string;
errors?: Record<string, string>;
errors?: { [field: string]: string }[] | { [field: string]: string };
values: Record<ActionId, { value: unknown } | undefined>;
viewId?: string;
rid?: string;

@ -57,7 +57,11 @@ export const useUiKitState = <TElement extends UiKit.ActionableElement>(
const { values, errors } = useContext(UiKitContext);
const _value = getElementValueFromState(actionId, values, initialValue);
const error = errors?.[actionId];
const error = Array.isArray(errors)
? errors.find((error) =>
Object.keys(error).find((key) => key === actionId)
)?.[actionId]
: errors?.[actionId];
const [value, setValue] = useSafely(useState(_value));
const [loading, setLoading] = useSafely(useState(false));

Loading…
Cancel
Save