Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to react-admin v5 #61

Merged
merged 12 commits into from
Jun 28, 2024
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ jobs:
- name: Use Node.js LTS
uses: actions/setup-node@v1
with:
node-version: '16.x'
node-version: '18.x'
- uses: bahmutov/npm-install@v1
- name: Build
run: make build
@@ -39,15 +39,15 @@ jobs:
- name: Use Node.js LTS
uses: actions/setup-node@v1
with:
node-version: '16.x'
node-version: '18.x'
- uses: bahmutov/npm-install@v1
- name: Prepare env
run: cp -n ./packages/demo/.env.local-example ./packages/demo/.env
- name: Build
run: make build build-demo
- uses: supabase/[email protected]
with:
version: 1.38.6
version: 1.131.2
- name: Start Supabase local development setup
run: supabase start
- name: Setup database
1 change: 1 addition & 0 deletions cypress/e2e/login.ts
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ export const login = (
email: string = '[email protected]',
password: string = 'password'
) => {
cy.wait(1000);
cy.findByLabelText('Email *').type(email);
cy.findByLabelText('Password *').type(password);
cy.findByText('Sign in').click();
24 changes: 24 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module.exports = {
globalSetup: './test-global-setup.js',
setupFilesAfterEnv: ['./test-setup.js'],
testEnvironment: 'jsdom',
testPathIgnorePatterns: [
'/node_modules/',
'/lib/',
'/esm/',
'/packages/demo',
],
transformIgnorePatterns: [
'[/\\\\]node_modules[/\\\\](?!(@hookform)/).+\\.(js|jsx|mjs|ts|tsx)$',
],
transform: {
// '^.+\\.[tj]sx?$' to process js/ts with `ts-jest`
'^.+\\.[tj]sx?$': [
'ts-jest',
{
isolatedModules: true,
useESM: true,
},
],
},
};
11 changes: 5 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -39,14 +39,16 @@
"prettier": "~2.8.8",
"raf": "^3.4.1",
"supabase": "^1.131.2",
"typescript": "^4.9.5"
"typescript": "^4.9.5",
"ts-jest": "^29.1.0",
"whatwg-fetch": "^3.0.0"
},
"scripts": {
"build": "lerna run build",
"build-demo": "cd packages/demo && yarn build",
"run-demo": "cd packages/demo && yarn start",
"run-demo-prod": "cd packages/demo && yarn serve",
"test-unit": "lerna run test-unit",
"test-unit": "jest",
"test-e2e": "cypress run",
"test-e2e-local": "cypress open",
"watch": "lerna run --parallel watch",
@@ -58,8 +60,5 @@
},
"workspaces": [
"packages/*"
],
"jest": {
"testEnvironment": "jsdom"
}
]
}
16 changes: 8 additions & 8 deletions packages/demo/package.json
Original file line number Diff line number Diff line change
@@ -4,30 +4,30 @@
"private": true,
"dependencies": {
"@mui/icons-material": "^5.0.1",
"@mui/material": "^5.0.2",
"@mui/material": "^5.15.20",
"@nivo/bar": "^0.80.0",
"@nivo/core": "^0.80.0",
"@tanstack/react-query": "^5.45.1",
"@vitejs/plugin-react": "^2.2.0",
"clsx": "^1.1.1",
"date-fns": "^2.19.0",
"faker": "~5.4.0",
"lodash": "~4.17.5",
"prop-types": "^15.7.2",
"ra-data-fakerest": "^4.0.0",
"ra-data-fakerest": "^5.0.0",
"ra-supabase": "^2.3.0",
"react": "^18.2.0",
"react-admin": "^4.0.0",
"react-admin": "^5.0.0",
"react-beautiful-dnd": "^13.0.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^3.1.4",
"react-query": "^3.32.1",
"react-router": "^6.1.0",
"react-router-dom": "^6.1.0",
"react-router": "^6.23.1",
"react-router-dom": "^6.23.1",
"vite": "^3.2.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/jest-dom": "^6.4.5",
"@testing-library/react": "^15.0.7",
"@testing-library/user-event": "^14.4.3",
"@types/faker": "^5.1.7",
"@types/jest": "^26.0.19",
2 changes: 1 addition & 1 deletion packages/demo/src/App.tsx
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ import {
SetPasswordPage,
ForgotPasswordPage,
} from 'ra-supabase';
import { QueryClient } from 'react-query';
import { QueryClient } from '@tanstack/react-query';
import { authProvider } from './authProvider';
import Layout from './Layout';
import contacts from './contacts';
23 changes: 10 additions & 13 deletions packages/ra-supabase-core/package.json
Original file line number Diff line number Diff line change
@@ -17,19 +17,19 @@
"peerDependencies": {
"@raphiniert/ra-data-postgrest": "^2.0.0",
"@supabase/supabase-js": "^2.0.0",
"ra-core": "^4.7.0"
"ra-core": "^5.0.1"
},
"devDependencies": {
"@raphiniert/ra-data-postgrest": "^2.1.0",
"@supabase/supabase-js": "^2.43.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
"ra-core": "^4.7.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.7.0",
"react-router-dom": "^6.7.0"
"@supabase/supabase-js": "^2.43.5",
"@testing-library/jest-dom": "^6.4.5",
"@testing-library/react": "^15.0.7",
"@testing-library/user-event": "^14.5.2",
"ra-core": "^5.0.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router": "^6.23.1",
"react-router-dom": "^6.23.1"
},
"scripts": {
"build": "yarn run build-cjs && yarn run build-esm",
@@ -38,8 +38,5 @@
"watch": "tsc --outDir esm --module es2015 --watch",
"lint": "eslint --fix ./src",
"test-unit": "jest ./src"
},
"jest": {
"testEnvironment": "jsdom"
}
}
2 changes: 1 addition & 1 deletion packages/ra-supabase-core/src/authProvider.ts
Original file line number Diff line number Diff line change
@@ -139,7 +139,7 @@ export const supabaseAuthProvider = (
return Promise.resolve();
},
async getPermissions() {
const { data, error } = await client.auth.getUser();
const { data, error } = await client.auth.getUser();
if (error) {
throw error;
}
79 changes: 50 additions & 29 deletions packages/ra-supabase-core/src/useRedirectIfAuthenticated.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react';
import { CoreAdminContext } from 'ra-core';
import { CoreAdminContext, TestMemoryRouter } from 'ra-core';
import { render, waitFor } from '@testing-library/react';
import { createMemoryHistory } from 'history';
import {
useRedirectIfAuthenticated,
UseRedirectIfAuthenticatedOptions,
@@ -29,19 +28,29 @@ describe('useRedirectIfAuthenticated', () => {
getPermissions: jest.fn(),
setPassword: jest.fn(),
};
const history = createMemoryHistory({ initialEntries: ['/login'] });
const push = jest.spyOn(history, 'push');

let location;
render(
<CoreAdminContext authProvider={authProvider} history={history}>
<UseRedirectIfAuthenticated />
</CoreAdminContext>
<TestMemoryRouter
initialEntries={['/login']}
locationCallback={l => {
location = l;
}}
>
<CoreAdminContext authProvider={authProvider}>
<UseRedirectIfAuthenticated />
</CoreAdminContext>
</TestMemoryRouter>
);

expect(authProvider.checkAuth).toHaveBeenCalled();
await waitFor(() => {
expect(push).toHaveBeenCalledTimes(0);
});
expect(location).toEqual(
expect.objectContaining({
hash: '',
pathname: '/login',
search: '',
})
);
});

test('should redirect users if they are authenticated', async () => {
@@ -53,25 +62,29 @@ describe('useRedirectIfAuthenticated', () => {
getPermissions: jest.fn(),
setPassword: jest.fn(),
};
const history = createMemoryHistory({ initialEntries: ['/login'] });
const push = jest.spyOn(history, 'push');

let location;
render(
<CoreAdminContext authProvider={authProvider} history={history}>
<UseRedirectIfAuthenticated />
</CoreAdminContext>
<TestMemoryRouter
initialEntries={['/login']}
locationCallback={l => {
location = l;
}}
>
<CoreAdminContext authProvider={authProvider}>
<UseRedirectIfAuthenticated />
</CoreAdminContext>
</TestMemoryRouter>
);

expect(authProvider.checkAuth).toHaveBeenCalled();
await waitFor(() => {
expect(push).toHaveBeenCalledWith(
{
expect(location).toEqual(
expect.objectContaining({
hash: '',
pathname: '/',
search: '',
},
undefined,
{}
})
);
});
});
@@ -85,21 +98,29 @@ describe('useRedirectIfAuthenticated', () => {
getPermissions: jest.fn(),
setPassword: jest.fn(),
};
const history = createMemoryHistory({ initialEntries: ['/login'] });
const push = jest.spyOn(history, 'push');

let location;
render(
<CoreAdminContext authProvider={authProvider} history={history}>
<UseRedirectIfAuthenticated redirectTo="/dashboard" />
</CoreAdminContext>
<TestMemoryRouter
initialEntries={['/login']}
locationCallback={l => {
location = l;
}}
>
<CoreAdminContext authProvider={authProvider}>
<UseRedirectIfAuthenticated redirectTo="/dashboard" />
</CoreAdminContext>
</TestMemoryRouter>
);

expect(authProvider.checkAuth).toHaveBeenCalled();
await waitFor(() => {
expect(push).toHaveBeenCalledWith(
{ hash: '', pathname: '/dashboard', search: '' },
undefined,
{}
expect(location).toEqual(
expect.objectContaining({
hash: '',
pathname: '/dashboard',
search: '',
})
);
});
});
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ export const useRedirectIfAuthenticated = (
const checkAuth = useCheckAuth();

useEffect(() => {
checkAuth({}, false, undefined, true)
checkAuth({}, false, undefined)
.then(() => {
// already authenticated, redirect to the home page
navigate(redirectTo);
16 changes: 9 additions & 7 deletions packages/ra-supabase-core/src/useResetPassword.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { onError, OnSuccess, useAuthProvider, useNotify } from 'ra-core';
import { useMutation, UseMutationResult } from 'react-query';
import { OnError, OnSuccess, useAuthProvider, useNotify } from 'ra-core';
import { useMutation, UseMutationResult } from '@tanstack/react-query';
import { ResetPasswordParams, SupabaseAuthProvider } from './authProvider';

/**
@@ -42,17 +42,19 @@ export const useResetPassword = (
onError = error => notify(error.message, { type: 'error' }),
} = options || {};

const mutation = useMutation<unknown, Error, ResetPasswordParams>(
params => {
const mutation = useMutation<unknown, Error, ResetPasswordParams>({
mutationFn: params => {
return authProvider.resetPassword(params);
},
{ onSuccess, onError, retry: false }
);
onSuccess,
onError,
retry: false,
});

return [mutation.mutate, mutation];
};

export type UseResetPasswordOptions = {
onSuccess?: OnSuccess;
onError?: onError;
onError?: OnError;
};
16 changes: 9 additions & 7 deletions packages/ra-supabase-core/src/useSetPassword.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
onError,
OnError,
OnSuccess,
useAuthProvider,
useNotify,
useRedirect,
} from 'ra-core';
import { useMutation, UseMutationResult } from 'react-query';
import { useMutation, UseMutationResult } from '@tanstack/react-query';
import { SetPasswordParams, SupabaseAuthProvider } from './authProvider';

/**
@@ -49,17 +49,19 @@ export const useSetPassword = (
onError = error => notify(error.message, { type: 'error' }),
} = options || {};

const mutation = useMutation<unknown, Error, SetPasswordParams>(
params => {
const mutation = useMutation<unknown, Error, SetPasswordParams>({
mutationFn: params => {
return authProvider.setPassword(params);
},
{ onSuccess, onError, retry: false }
);
onSuccess,
onError,
retry: false,
});

return [mutation.mutate, mutation];
};

export type UseSetPasswordOptions = {
onSuccess?: OnSuccess;
onError?: onError;
onError?: OnError;
};
64 changes: 29 additions & 35 deletions packages/ra-supabase-core/src/useSupabaseAccessToken.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react';
import { render, waitFor } from '@testing-library/react';
import { createMemoryHistory } from 'history';
import { CoreAdminContext } from 'ra-core';
import { CoreAdminContext, TestMemoryRouter } from 'ra-core';
import {
useSupabaseAccessToken,
UseSupabaseAccessTokenOptions,
@@ -21,14 +20,13 @@ describe.skip('useSupabaseAccessToken', () => {
'React Admin',
'/set-password?access_token=bazinga'
);
const history = createMemoryHistory({
initialEntries: ['/set-password'],
});

const { queryByText } = render(
<CoreAdminContext history={history}>
<UseSupabaseAccessToken />
</CoreAdminContext>
<TestMemoryRouter initialEntries={['/set-password']}>
<CoreAdminContext>
<UseSupabaseAccessToken />
</CoreAdminContext>
</TestMemoryRouter>
);

await waitFor(() => {
@@ -42,14 +40,13 @@ describe.skip('useSupabaseAccessToken', () => {
'React Admin',
'/set-password?my_token=bazinga'
);
const history = createMemoryHistory({
initialEntries: ['/set-password'],
});

const { queryByText } = render(
<CoreAdminContext history={history}>
<UseSupabaseAccessToken parameterName="my_token" />
</CoreAdminContext>
<TestMemoryRouter initialEntries={['/set-password']}>
<CoreAdminContext>
<UseSupabaseAccessToken parameterName="my_token" />
</CoreAdminContext>
</TestMemoryRouter>
);

await waitFor(() => {
@@ -59,52 +56,49 @@ describe.skip('useSupabaseAccessToken', () => {

test('should redirect users if the access token is not present in the URL', async () => {
window.history.pushState({}, 'React Admin', '/set-password');
const history = createMemoryHistory({
initialEntries: ['/set-password'],
});

render(
<CoreAdminContext history={history}>
<UseSupabaseAccessToken />
</CoreAdminContext>
<TestMemoryRouter initialEntries={['/set-password']}>
<CoreAdminContext>
<UseSupabaseAccessToken />
</CoreAdminContext>
</TestMemoryRouter>
);

await waitFor(() => {
expect(history.location.pathname).toEqual('/');
expect(window.location.pathname).toEqual('/');
});
});

test('should redirect users to the provided path if the access token is not present in the URL', async () => {
window.history.pushState({}, 'React Admin', '/set-password');
const history = createMemoryHistory({
initialEntries: ['/set-password'],
});

render(
<CoreAdminContext history={history}>
<UseSupabaseAccessToken redirectTo="/login" />
</CoreAdminContext>
<TestMemoryRouter initialEntries={['/set-password']}>
<CoreAdminContext>
<UseSupabaseAccessToken redirectTo="/login" />
</CoreAdminContext>
</TestMemoryRouter>
);

await waitFor(() => {
expect(history.location.pathname).toEqual('/login');
expect(window.location.pathname).toEqual('/login');
});
});

test('should not redirect users if the access token is not present in the URL and redirectTo is false', async () => {
window.history.pushState({}, 'React Admin', '/set-password');
const history = createMemoryHistory({
initialEntries: ['/set-password'],
});

render(
<CoreAdminContext history={history}>
<UseSupabaseAccessToken redirectTo={false} />
</CoreAdminContext>
<TestMemoryRouter initialEntries={['/set-password']}>
<CoreAdminContext>
<UseSupabaseAccessToken redirectTo={false} />
</CoreAdminContext>
</TestMemoryRouter>
);

await waitFor(() => {
expect(history.location.pathname).toEqual('/set-password');
expect(window.location.pathname).toEqual('/set-password');
});
});
});
26 changes: 13 additions & 13 deletions packages/ra-supabase-ui-materialui/package.json
Original file line number Diff line number Diff line change
@@ -18,24 +18,24 @@
"ra-supabase-core": "^2.3.0"
},
"devDependencies": {
"@mui/icons-material": "^5.11.0",
"@mui/material": "^5.11.6",
"@supabase/supabase-js": "^2.4.1",
"ra-core": "^4.7.0",
"ra-ui-materialui": "^4.7.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.7.0"
"@mui/icons-material": "^5.15.20",
"@mui/material": "^5.15.20",
"@supabase/supabase-js": "^2.43.5",
"ra-core": "^5.0.1",
"ra-ui-materialui": "^5.0.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router": "^6.23.1"
},
"peerDependencies": {
"@mui/icons-material": "^5.0.0",
"@mui/material": "^5.0.0",
"@supabase/supabase-js": "^2.0.0",
"ra-core": "^4.7.0",
"ra-ui-materialui": "^4.7.1",
"react": "^16.9.0 || ^18.0.0",
"react-dom": "^16.9.0 || ^18.0.0",
"react-router": "^6.1.0"
"ra-core": "^5.0.1",
"ra-ui-materialui": "^5.0.1",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-router": "^6.23.1"
},
"scripts": {
"build": "yarn run build-cjs && yarn run build-esm",
3 changes: 3 additions & 0 deletions test-global-setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = async () => {
process.env.TZ = 'Europe/Paris';
};
19 changes: 19 additions & 0 deletions test-setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Ignore warnings about act()
// See https://github.com/testing-library/react-testing-library/issues/281,
// https://github.com/facebook/react/issues/14769
const originalError = console.error;
jest.spyOn(console, 'error').mockImplementation((...args) => {
if (/Warning.*not wrapped in act/.test(args[0])) {
return;
}
originalError.call(console, ...args);
});

/**
* Mock fetch objects Response, Request and Headers
*/
const { Response, Headers, Request } = require('whatwg-fetch');

global.Response = Response;
global.Headers = Headers;
global.Request = Request;
4 changes: 2 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -8,13 +8,13 @@
] /* Specify library files to be included in the compilation. */,
"declaration": true,
"declarationMap": true,
"allowJs": false,
"allowJs": true,
"jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
"noImplicitAny": false /* Raise error on expressions and declarations with an implied 'any' type. */,
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
"allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
"skipLibCheck": true,
"skipLibCheck": true
}
}
939 changes: 424 additions & 515 deletions yarn.lock

Large diffs are not rendered by default.


Unchanged files with check annotations Beta

date: randomDate(
new Date(
db.companies.find(
company => company.id == deal.company_id

Check warning on line 19 in packages/demo/src/dataGenerator/dealNotes.ts

GitHub Actions / unit-test

Expected '===' and instead saw '=='
).created_at
)
).toISOString(),
import { Db } from './types';

Check warning on line 1 in packages/demo/src/dataGenerator/tags.ts

GitHub Actions / unit-test

'Db' is defined but never used
// --champagne-pink: #eddcd2ff;
// --linen: #fff1e6ff;