feat: "Add content" sidebar on each library home page (#1065)

This commit is contained in:
Chris Chávez
2024-07-17 03:15:40 -05:00
committed by GitHub
parent e087001905
commit 3d24741062
20 changed files with 803 additions and 91 deletions

View File

@@ -0,0 +1,69 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { initializeMockApp } from '@edx/frontend-platform';
import { AppProvider } from '@edx/frontend-platform/react';
import { ToastContext, ToastProvider } from '.';
import initializeStore from '../../store';
export interface WraperProps {
children: React.ReactNode;
}
const TestComponentToShow = () => {
const { showToast } = React.useContext(ToastContext);
React.useEffect(() => {
showToast('This is the toast!');
}, [showToast]);
return <div>Content</div>;
};
const TestComponentToClose = () => {
const { showToast, closeToast } = React.useContext(ToastContext);
React.useEffect(() => {
showToast('This is the toast!');
closeToast();
}, [showToast]);
return <div>Content</div>;
};
let store;
const RootWrapper = ({ children }: WraperProps) => (
<AppProvider store={store}>
<ToastProvider>
{children}
</ToastProvider>
</AppProvider>
);
describe('<ToastProvider />', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
});
afterEach(() => {
jest.clearAllMocks();
});
it('should show toast', async () => {
render(<RootWrapper><TestComponentToShow /></RootWrapper>);
expect(await screen.findByText('This is the toast!')).toBeInTheDocument();
});
it('should close toast', async () => {
render(<RootWrapper><TestComponentToClose /></RootWrapper>);
expect(await screen.findByText('Content')).toBeInTheDocument();
expect(screen.queryByText('This is the toast!')).not.toBeInTheDocument();
});
});

View File

@@ -0,0 +1,57 @@
import React from 'react';
import { Toast } from '@openedx/paragon';
export interface ToastContextData {
toastMessage: string | null;
showToast: (message: string) => void;
closeToast: () => void;
}
export interface ToastProviderProps {
children: React.ReactNode;
}
/**
* Global context to keep track of popup message(s) that appears to user after
* they take an action like creating or deleting something.
*/
export const ToastContext = React.createContext({
toastMessage: null,
showToast: () => {},
closeToast: () => {},
} as ToastContextData);
/**
* React component to provide `ToastContext` to the app
*/
export const ToastProvider = (props: ToastProviderProps) => {
// TODO, We can convert this to a queue of messages,
// see: https://github.com/open-craft/frontend-app-course-authoring/pull/38#discussion_r1638990647
const [toastMessage, setToastMessage] = React.useState<string | null>(null);
React.useEffect(() => () => {
// Cleanup function to avoid updating state on unmounted component
setToastMessage(null);
}, []);
const showToast = React.useCallback((message) => setToastMessage(message), [setToastMessage]);
const closeToast = React.useCallback(() => setToastMessage(null), [setToastMessage]);
const context = React.useMemo(() => ({
toastMessage,
showToast,
closeToast,
}), [toastMessage, showToast, closeToast]);
return (
<ToastContext.Provider value={context}>
{props.children}
{ toastMessage && (
<Toast show={toastMessage !== null} onClose={closeToast}>
{toastMessage}
</Toast>
)}
</ToastContext.Provider>
);
};