mirror of
https://github.com/strapi/strapi.git
synced 2025-10-19 03:49:45 +00:00
chore: useScopedPersistentState custom hook
This commit is contained in:
parent
8ecdda9ea1
commit
83c9d185c6
@ -7,7 +7,7 @@ import { useIntl } from 'react-intl';
|
|||||||
import { styled } from 'styled-components';
|
import { styled } from 'styled-components';
|
||||||
|
|
||||||
import { useGetLicenseTrialTimeLeftQuery } from '../../src/services/admin';
|
import { useGetLicenseTrialTimeLeftQuery } from '../../src/services/admin';
|
||||||
import { usePersistentState } from '../hooks/usePersistentState';
|
import { useScopedPersistentState } from '../hooks/usePersistentState';
|
||||||
|
|
||||||
const BannerBackground = styled(Flex)`
|
const BannerBackground = styled(Flex)`
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
@ -101,7 +101,7 @@ const Banner = ({ isTrialEndedRecently }: { isTrialEndedRecently: boolean }) =>
|
|||||||
const UpsellBanner = () => {
|
const UpsellBanner = () => {
|
||||||
const { license } = useLicenseLimits();
|
const { license } = useLicenseLimits();
|
||||||
|
|
||||||
const [cachedTrialEndsAt, setCachedTrialEndsAt] = usePersistentState<string | undefined>(
|
const [cachedTrialEndsAt, setCachedTrialEndsAt] = useScopedPersistentState<string | undefined>(
|
||||||
'STRAPI_FREE_TRIAL_ENDS_AT',
|
'STRAPI_FREE_TRIAL_ENDS_AT',
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
|
@ -18,11 +18,16 @@ jest.mock('../../../src/services/admin', () => ({
|
|||||||
trialEndsAt: '2025-05-15T00:00:00.000Z',
|
trialEndsAt: '2025-05-15T00:00:00.000Z',
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
|
useInitQuery: jest.fn(() => ({
|
||||||
|
data: {
|
||||||
|
uuid: 'test-uuid',
|
||||||
|
},
|
||||||
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('UpsellBanner', () => {
|
describe('UpsellBanner', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
localStorage.removeItem('STRAPI_FREE_TRIAL_ENDS_AT');
|
localStorage.removeItem('STRAPI_FREE_TRIAL_ENDS_AT:test-uuid');
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
@ -82,7 +87,7 @@ describe('UpsellBanner', () => {
|
|||||||
data: {},
|
data: {},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
localStorage.setItem('STRAPI_FREE_TRIAL_ENDS_AT', '2025-05-21T09:50:00.000Z');
|
localStorage.setItem('STRAPI_FREE_TRIAL_ENDS_AT:test-uuid', '2025-05-21T09:50:00.000Z');
|
||||||
jest.setSystemTime(new Date(2025, 4, 22));
|
jest.setSystemTime(new Date(2025, 4, 22));
|
||||||
|
|
||||||
render(<UpsellBanner />);
|
render(<UpsellBanner />);
|
||||||
@ -114,7 +119,7 @@ describe('UpsellBanner', () => {
|
|||||||
data: {},
|
data: {},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
localStorage.setItem('STRAPI_FREE_TRIAL_ENDS_AT', '2025-05-10T09:50:00.000Z');
|
localStorage.setItem('STRAPI_FREE_TRIAL_ENDS_AT:test-uuid', '2025-05-10T09:50:00.000Z');
|
||||||
jest.setSystemTime(new Date(2025, 4, 22));
|
jest.setSystemTime(new Date(2025, 4, 22));
|
||||||
|
|
||||||
render(<UpsellBanner />);
|
render(<UpsellBanner />);
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
import { renderHook, act } from '@testing-library/react';
|
import { renderHook, act } from '@testing-library/react';
|
||||||
|
|
||||||
import { usePersistentState } from '../usePersistentState';
|
import { usePersistentState, useScopedPersistentState } from '../usePersistentState';
|
||||||
|
|
||||||
|
jest.mock('../../services/admin', () => ({
|
||||||
|
useInitQuery: jest.fn(() => ({
|
||||||
|
data: {
|
||||||
|
uuid: 'test-uuid',
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('usePersistentState', () => {
|
describe('usePersistentState', () => {
|
||||||
it('should return the value passed to set in the local storage', async () => {
|
it('should return the value passed to set in the local storage', async () => {
|
||||||
@ -15,3 +23,18 @@ describe('usePersistentState', () => {
|
|||||||
expect(updatedValue).toBe(1);
|
expect(updatedValue).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('useScopedPersistentState', () => {
|
||||||
|
it('should return the value passed to set in the local storage with a scoped key', async () => {
|
||||||
|
const { result } = renderHook(() => useScopedPersistentState('key', 0));
|
||||||
|
const [value, setValue] = result.current;
|
||||||
|
expect(value).toBe(0);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
setValue(1);
|
||||||
|
});
|
||||||
|
const [updatedValue] = result.current;
|
||||||
|
expect(localStorage.getItem('key:test-uuid')).toBeDefined();
|
||||||
|
expect(updatedValue).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { useInitQuery } from '../services/admin';
|
||||||
|
|
||||||
const usePersistentState = <T>(key: string, defaultValue: T) => {
|
const usePersistentState = <T>(key: string, defaultValue: T) => {
|
||||||
const [value, setValue] = useState<T>(() => {
|
const [value, setValue] = useState<T>(() => {
|
||||||
const stickyValue = window.localStorage.getItem(key);
|
const stickyValue = window.localStorage.getItem(key);
|
||||||
@ -23,4 +25,14 @@ const usePersistentState = <T>(key: string, defaultValue: T) => {
|
|||||||
return [value, setValue] as const;
|
return [value, setValue] as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { usePersistentState };
|
// Same as usePersistentState, but scoped to the current instance of Strapi
|
||||||
|
// useful for storing state that should not be shared across different instances of Strapi running on localhost
|
||||||
|
const useScopedPersistentState = <T>(key: string, defaultValue: T) => {
|
||||||
|
const { data: initData } = useInitQuery();
|
||||||
|
const { uuid } = initData ?? {};
|
||||||
|
|
||||||
|
const namespacedKey = `${key}:${uuid}`;
|
||||||
|
return usePersistentState<T>(namespacedKey, defaultValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { usePersistentState, useScopedPersistentState };
|
||||||
|
@ -7,7 +7,7 @@ import { useIntl } from 'react-intl';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { useLicenseLimits } from '../../../../../ee/admin/src/hooks/useLicenseLimits';
|
import { useLicenseLimits } from '../../../../../ee/admin/src/hooks/useLicenseLimits';
|
||||||
import { usePersistentState } from '../../../hooks/usePersistentState';
|
import { useScopedPersistentState } from '../../../hooks/usePersistentState';
|
||||||
|
|
||||||
const StyledModalContent = styled(Modal.Content)`
|
const StyledModalContent = styled(Modal.Content)`
|
||||||
max-width: 51.6rem;
|
max-width: 51.6rem;
|
||||||
@ -30,11 +30,11 @@ const StyledButton = styled(Button)`
|
|||||||
export const FreeTrialEndedModal = () => {
|
export const FreeTrialEndedModal = () => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const [open, setOpen] = useState(true);
|
const [open, setOpen] = useState(true);
|
||||||
const [previouslyOpen, setPreviouslyOpen] = usePersistentState(
|
const [previouslyOpen, setPreviouslyOpen] = useScopedPersistentState(
|
||||||
'STRAPI_FREE_TRIAL_ENDED_MODAL',
|
'STRAPI_FREE_TRIAL_ENDED_MODAL',
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
const [cachedTrialEndsAt] = usePersistentState<string | undefined>(
|
const [cachedTrialEndsAt] = useScopedPersistentState<string | undefined>(
|
||||||
'STRAPI_FREE_TRIAL_ENDS_AT',
|
'STRAPI_FREE_TRIAL_ENDS_AT',
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
|
@ -7,7 +7,7 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import { useLicenseLimits } from '../../../../../ee/admin/src/hooks/useLicenseLimits';
|
import { useLicenseLimits } from '../../../../../ee/admin/src/hooks/useLicenseLimits';
|
||||||
import lightIllustration from '../../../assets/images/free-trial.png';
|
import lightIllustration from '../../../assets/images/free-trial.png';
|
||||||
import { usePersistentState } from '../../../hooks/usePersistentState';
|
import { useScopedPersistentState } from '../../../hooks/usePersistentState';
|
||||||
|
|
||||||
const StyledModalContent = styled(Modal.Content)`
|
const StyledModalContent = styled(Modal.Content)`
|
||||||
max-width: 51.6rem;
|
max-width: 51.6rem;
|
||||||
@ -34,7 +34,7 @@ const StyledButton = styled(Button)`
|
|||||||
export const FreeTrialWelcomeModal = () => {
|
export const FreeTrialWelcomeModal = () => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const [open, setOpen] = useState(true);
|
const [open, setOpen] = useState(true);
|
||||||
const [previouslyOpen, setPreviouslyOpen] = usePersistentState(
|
const [previouslyOpen, setPreviouslyOpen] = useScopedPersistentState(
|
||||||
'STRAPI_FREE_TRIAL_WELCOME_MODAL',
|
'STRAPI_FREE_TRIAL_WELCOME_MODAL',
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
@ -18,12 +18,17 @@ jest.mock('../../../../../src/services/admin', () => ({
|
|||||||
trialEndsAt: '2025-05-15T00:00:00.000Z',
|
trialEndsAt: '2025-05-15T00:00:00.000Z',
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
|
useInitQuery: jest.fn(() => ({
|
||||||
|
data: {
|
||||||
|
uuid: 'test-uuid',
|
||||||
|
},
|
||||||
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('FreeTrialEndedModal', () => {
|
describe('FreeTrialEndedModal', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
localStorage.removeItem('STRAPI_FREE_TRIAL_ENDS_AT');
|
localStorage.removeItem('STRAPI_FREE_TRIAL_ENDS_AT:test-uuid');
|
||||||
localStorage.removeItem('STRAPI_FREE_TRIAL_ENDED_MODAL');
|
localStorage.removeItem('STRAPI_FREE_TRIAL_ENDED_MODAL:test-uuid');
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
@ -36,7 +41,7 @@ describe('FreeTrialEndedModal', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should render when trial ended less than 7 days ago and modal never appeared before', async () => {
|
it('should render when trial ended less than 7 days ago and modal never appeared before', async () => {
|
||||||
localStorage.setItem('STRAPI_FREE_TRIAL_ENDS_AT', '2025-05-21T09:50:00.000Z');
|
localStorage.setItem('STRAPI_FREE_TRIAL_ENDS_AT:test-uuid', '2025-05-21T09:50:00.000Z');
|
||||||
|
|
||||||
// @ts-expect-error – mock
|
// @ts-expect-error – mock
|
||||||
useLicenseLimits.mockImplementationOnce(() => ({
|
useLicenseLimits.mockImplementationOnce(() => ({
|
||||||
@ -53,8 +58,8 @@ describe('FreeTrialEndedModal', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not render when trial ended less than 7 days ago but modal already appeared before', async () => {
|
it('should not render when trial ended less than 7 days ago but modal already appeared before', async () => {
|
||||||
localStorage.setItem('STRAPI_FREE_TRIAL_ENDS_AT', '2025-05-21T09:50:00.000Z');
|
localStorage.setItem('STRAPI_FREE_TRIAL_ENDS_AT:test-uuid', '2025-05-21T09:50:00.000Z');
|
||||||
localStorage.setItem('STRAPI_FREE_TRIAL_ENDED_MODAL', 'true');
|
localStorage.setItem('STRAPI_FREE_TRIAL_ENDED_MODAL:test-uuid', 'true');
|
||||||
|
|
||||||
// @ts-expect-error – mock
|
// @ts-expect-error – mock
|
||||||
useLicenseLimits.mockImplementationOnce(() => ({
|
useLicenseLimits.mockImplementationOnce(() => ({
|
||||||
|
@ -3,6 +3,14 @@ import { render, screen, waitFor } from '@tests/utils';
|
|||||||
import { useLicenseLimits } from '../../../../../../ee/admin/src/hooks/useLicenseLimits';
|
import { useLicenseLimits } from '../../../../../../ee/admin/src/hooks/useLicenseLimits';
|
||||||
import { FreeTrialWelcomeModal } from '../FreeTrialWelcomeModal';
|
import { FreeTrialWelcomeModal } from '../FreeTrialWelcomeModal';
|
||||||
|
|
||||||
|
jest.mock('../../../../services/admin', () => ({
|
||||||
|
useInitQuery: jest.fn(() => ({
|
||||||
|
data: {
|
||||||
|
uuid: 'test-uuid',
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
jest.mock('../../../../../../ee/admin/src/hooks/useLicenseLimits', () => ({
|
jest.mock('../../../../../../ee/admin/src/hooks/useLicenseLimits', () => ({
|
||||||
useLicenseLimits: jest.fn(() => ({
|
useLicenseLimits: jest.fn(() => ({
|
||||||
license: {
|
license: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user