diff --git a/packages/dev/src/AppCustomDrawer.tsx b/packages/dev/src/AppCustomDrawer.tsx new file mode 100644 index 00000000..5f6af2ff --- /dev/null +++ b/packages/dev/src/AppCustomDrawer.tsx @@ -0,0 +1,175 @@ +import './App.css'; +import { + Page, + Button, + Drawer, + DrawerContent, + DrawerPanelContent, + DrawerHead, + DrawerActions, + DrawerCloseButton, + DrawerPanelDescription, + DrawerPanelBody, + DrawerContentBody, +} from '@patternfly/react-core'; +import { + LoadingBox, + QuickStartContainer, + QuickStartContainerProps, + QuickStartDrawerContent, + QuickStartCloseModal, + QuickStartStatus, + useLocalStorage, + setQueryArgument, + removeQueryArgument, + QUICKSTART_ID_FILTER_KEY, +} from '@patternfly/quickstarts'; +import { allQuickStarts as yamlQuickStarts } from './quickstarts-data/quick-start-test-data'; +import React from 'react'; +import i18n from './i18n/i18n'; +import { AppHeader, AppSidebar } from './common/Page'; + +interface AppProps { + children?: React.ReactNode; + showCardFooters?: boolean; +} + +const App: React.FC = ({ children, showCardFooters }) => { + const [activeQuickStartID, setActiveQuickStartID] = useLocalStorage('quickstartId', ''); + const [allQuickStartStates, setAllQuickStartStates] = useLocalStorage('quickstarts', {}); + const language = localStorage.getItem('bridge/language') || 'en'; + const resourceBundle = i18n.getResourceBundle(language, 'quickstart'); + + // eslint-disable-next-line no-console + React.useEffect(() => console.log(activeQuickStartID), [activeQuickStartID]); + React.useEffect(() => { + // callback on state change + // eslint-disable-next-line no-console + console.log(allQuickStartStates); + }, [allQuickStartStates]); + + const withQueryParams = true; + + const containerProps: QuickStartContainerProps = { + quickStarts: yamlQuickStarts, + activeQuickStartID, + allQuickStartStates, + setActiveQuickStartID, + setAllQuickStartStates, + resourceBundle, + showCardFooters, + language, + useQueryParams: withQueryParams, + alwaysShowTaskReview: true, + markdown: { + extensions: [ + // variable substitution + { + type: 'output', + filter(html: string) { + html = html.replace(/\[APPLICATION\]/g, 'Mercury'); + html = html.replace(/\[PRODUCT\]/g, 'Lightning'); + + return html; + }, + }, + ], + }, + }; + + const toggleQuickStart = (quickStartId: string) => { + if (activeQuickStartID !== quickStartId) { + // activate + setActiveQuickStartID(quickStartId); + // optionally add the query param + withQueryParams && setQueryArgument(QUICKSTART_ID_FILTER_KEY, quickStartId); + } else { + // deactivate + setActiveQuickStartID(''); + // optionally remove the query param + withQueryParams && removeQueryArgument(QUICKSTART_ID_FILTER_KEY); + } + }; + + const [modalOpen, setModalOpen] = React.useState(false); + const onClose = () => { + setActiveQuickStartID(''); + setDrawerContent('none'); + }; + const handleClose = (activeQuickStartStatus: string | number) => { + if (activeQuickStartStatus === QuickStartStatus.IN_PROGRESS) { + setModalOpen(true); + } else { + onClose(); + } + onClose(); + }; + const onModalConfirm = () => { + setModalOpen(false); + onClose(); + }; + const onModalCancel = () => setModalOpen(false); + + const [drawerContent, setDrawerContent] = React.useState('none'); + + const otherDrawerPanelContent = ( + + + Drawer panel header + + setDrawerContent('none')} /> + + + Drawer panel description + Drawer panel body + + ); + + return ( + }> + + + + ) : ( + otherDrawerPanelContent + ) + } + > + + + + + {children} + + + + + + + + ); +}; +export default App; diff --git a/packages/dev/src/Nav.ts b/packages/dev/src/Nav.ts index 1ed057ff..f4701149 100644 --- a/packages/dev/src/Nav.ts +++ b/packages/dev/src/Nav.ts @@ -35,6 +35,11 @@ export const Nav: NavInterface[] = [ name: 'In-app documentation', to: '/in-app-documentation', }, + { + id: 'custom-drawer', + name: 'With custom drawer', + to: '/quickstarts-drawer', + }, ]; export default Nav; diff --git a/packages/dev/src/index.tsx b/packages/dev/src/index.tsx index 99ee4833..8b6c8468 100755 --- a/packages/dev/src/index.tsx +++ b/packages/dev/src/index.tsx @@ -11,6 +11,7 @@ import AppContext from './AppContext'; import AppProps from './AppProps'; import AppLocalized from './AppLocalized'; import AppHelpTopicDemo from './AppHelpTopicDemo'; +import AppCustomDrawer from './AppCustomDrawer'; import { DefaultCatalog } from './DefaultCatalog'; import { CustomCatalog } from './CustomCatalog'; import { MockConsole } from './MockConsole'; @@ -61,6 +62,14 @@ root.render( } /> + + + + } + /> , ); diff --git a/packages/module/patternfly-docs/content/extensions/quick-starts/examples/WithCustomDrawer.jsx b/packages/module/patternfly-docs/content/extensions/quick-starts/examples/WithCustomDrawer.jsx new file mode 100644 index 00000000..847a24cb --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/quick-starts/examples/WithCustomDrawer.jsx @@ -0,0 +1,158 @@ +import React from 'react'; +import { + LoadingBox, + QuickStartContainer, + useLocalStorage, + QuickStartCatalogPage, + QuickStartDrawerContent, + QuickStartCloseModal, + QuickStartStatus, +} from '@patternfly/quickstarts'; +import { + Drawer, + DrawerContent, + DrawerPanelContent, + DrawerHead, + DrawerActions, + DrawerCloseButton, + DrawerPanelDescription, + DrawerPanelBody, + DrawerContentBody, + Button, +} from '@patternfly/react-core'; +import { quickStarts as exampleQuickStarts } from './example-data'; + +export const App = ({ showCardFooters }) => { + const [activeQuickStartID, setActiveQuickStartID] = useLocalStorage('quickstartId', ''); + const [allQuickStartStates, setAllQuickStartStates] = useLocalStorage('quickstarts', {}); + const language = localStorage.getItem('bridge/language') || 'en'; + + // eslint-disable-next-line no-console + React.useEffect(() => console.log(activeQuickStartID), [activeQuickStartID]); + React.useEffect(() => { + // callback on state change + // eslint-disable-next-line no-console + console.log(allQuickStartStates); + }, [allQuickStartStates]); + + const [loading, setLoading] = React.useState(true); + const [quickStarts, setQuickStarts] = React.useState([]); + React.useEffect(() => { + const load = async () => { + setQuickStarts(exampleQuickStarts); + setLoading(false); + }; + setTimeout(() => { + load(); + }, 500); + }, []); + + const drawerProps = { + quickStarts, + activeQuickStartID, + allQuickStartStates, + setActiveQuickStartID, + setAllQuickStartStates, + showCardFooters, + language, + loading, + alwaysShowTaskReview: true, + markdown: { + extensions: [ + // variable substitution + { + type: 'output', + filter(html) { + html = html.replace(/\[APPLICATION\]/g, 'Mercury'); + html = html.replace(/\[PRODUCT\]/g, 'Lightning'); + + return html; + }, + }, + ], + }, + }; + + const [modalOpen, setModalOpen] = React.useState(false); + const onClose = () => { + setActiveQuickStartID(''); + setDrawerContent('none'); + }; + const handleClose = (activeQuickStartStatus) => { + if (activeQuickStartStatus === QuickStartStatus.IN_PROGRESS) { + setModalOpen(true); + } else { + onClose(); + } + }; + const onModalConfirm = () => { + setModalOpen(false); + onClose(); + }; + const onModalCancel = () => setModalOpen(false); + + const [drawerContent, setDrawerContent] = React.useState('none'); + + const otherDrawerPanelContent = ( + + + Drawer panel header + + setDrawerContent('none')} /> + + + Drawer panel description + Drawer panel body + + ); + + return ( + }> + + + + ) : ( + otherDrawerPanelContent + ) + } + > + + + + + + + + + + + ); +}; diff --git a/packages/module/patternfly-docs/content/extensions/quick-starts/examples/basic.md b/packages/module/patternfly-docs/content/extensions/quick-starts/examples/basic.md index 3914e2f9..28c94cb8 100644 --- a/packages/module/patternfly-docs/content/extensions/quick-starts/examples/basic.md +++ b/packages/module/patternfly-docs/content/extensions/quick-starts/examples/basic.md @@ -14,16 +14,29 @@ sourceLink: https://github.com/patternfly/patternfly-quickstarts/tree/main/packa --- import { quickStarts as exampleQuickStarts } from './example-data'; -import { LoadingBox, QuickStartContainer, QuickStartCatalogPage, useLocalStorage, } from '@patternfly/quickstarts'; +import { LoadingBox, QuickStartContainer, QuickStartCatalogPage, useLocalStorage, QuickStartDrawerContent, QuickStartCloseModal, QuickStartStatus, } from '@patternfly/quickstarts'; import '@patternfly/quickstarts/dist/quickstarts.css'; -## Basic quick starts examples +## Basic quick starts examples + +### Quick starts catalog -### Quick starts catalog ```js file="./Basic.jsx" + ``` ### Fullscreen catalog page + To view a fullscreen example, click the image below. + ```js file="./Basic.jsx" isFullscreen + +``` + +### Quick starts with custom drawer + +Quick starts may be placed into a nonmanaged, custom drawer. To view a fullscreen example, click the image below. + +```js file="./WithCustomDrawer.jsx" isFullscreen + ``` diff --git a/packages/module/src/QuickStartContainer.tsx b/packages/module/src/QuickStartContainer.tsx new file mode 100644 index 00000000..3ae3a23f --- /dev/null +++ b/packages/module/src/QuickStartContainer.tsx @@ -0,0 +1,142 @@ +import * as React from 'react'; +import { + QuickStartContext, + QuickStartContextValues, + useValuesForQuickStartContext, +} from './utils/quick-start-context'; +import QuickStartDrawer from './QuickStartDrawer'; +import { QuickStart, AllQuickStartStates } from './utils/quick-start-types'; + +export interface QuickStartContainerProps extends React.HTMLProps { + /** array of quick starts */ + quickStarts: QuickStart[]; + /** id of the currently active quick start */ + activeQuickStartID: string; + /** setter for the active quick starts */ + setActiveQuickStartID: React.Dispatch>; + /** quick starts state object */ + allQuickStartStates: AllQuickStartStates; + /** setter for the quick starts state object */ + setAllQuickStartStates: React.Dispatch>; + /** content to render within the container */ + children?: React.ReactNode; + /** element to render the drawer panel into */ + appendTo?: HTMLElement | (() => HTMLElement); + /** if true, the panel will take up the full browser width */ + fullWidth?: boolean; + /** callback when an in-progress quick start is closed */ + onCloseInProgress?: any; + /** callback when a not-in-progress quick start is closed */ + onCloseNotInProgress?: any; + /** true to show footer buttons in the catalog tiles (default true) */ + showCardFooters?: boolean; + /** true to use legacy drawer header variant colors */ + useLegacyHeaderColors?: boolean; + /** text resources object */ + resourceBundle?: any; + /** language of the current resource bundle + * Add custom strings: https://github.com/patternfly/patternfly-quickstarts/tree/main/packages/module#localization + */ + language?: string; + /** if true, will show a loading spinner on the catalog page (default false) */ + loading?: boolean; + /** if true, will update the browser URL with ?quickstart={ID} (default true) */ + useQueryParams?: boolean; + /** if true, the TaskReview component, aka "Check your work", will be shown regardless of task status */ + alwaysShowTaskReview?: boolean; + /** + * Additional markdown extensions and renderers to use + * Example usage: https://github.com/patternfly/patternfly-quickstarts/tree/main/packages/module#markdown-extensions + */ + markdown?: { + extensions?: any[]; + renderExtension?: (docContext: Document, rootSelector: string) => React.ReactNode; + }; + /** additional quick start context props */ + contextProps?: QuickStartContextValues; + /** Indicates that QuickStart should create and maintain its own drawer. If false, a QuickStartDrawerContent component should be present within the children, replacing the custom Drawer's DrawerContent. */ + isManagedDrawer?: boolean; +} + +export const QuickStartContainer: React.FC = ({ + quickStarts, + children, + activeQuickStartID, + allQuickStartStates, + setActiveQuickStartID, + setAllQuickStartStates, + appendTo, + fullWidth, + onCloseInProgress, + onCloseNotInProgress, + resourceBundle, + showCardFooters, + useLegacyHeaderColors, + language, + loading = false, + useQueryParams = true, + markdown, + contextProps, + alwaysShowTaskReview = true, + isManagedDrawer = true, + ...props +}: QuickStartContainerProps) => { + const valuesForQuickstartContext: QuickStartContextValues = useValuesForQuickStartContext({ + allQuickStarts: quickStarts, + activeQuickStartID, + setActiveQuickStartID, + allQuickStartStates, + setAllQuickStartStates, + footer: { + show: showCardFooters, + }, + useLegacyHeaderColors, + language, + resourceBundle: { + ...resourceBundle, + // Start: "Let's go!", + // Continue: 'Resume', + // Restart: 'Start over', + }, + loading, + useQueryParams, + markdown, + alwaysShowTaskReview, + ...contextProps, + }); + + React.useEffect(() => { + if ( + quickStarts && + JSON.stringify(quickStarts) !== JSON.stringify(valuesForQuickstartContext.allQuickStarts) + ) { + valuesForQuickstartContext.setAllQuickStarts(quickStarts); + } + }, [quickStarts, valuesForQuickstartContext]); + + React.useEffect(() => { + if (loading !== valuesForQuickstartContext.loading) { + valuesForQuickstartContext.setLoading(loading); + } + }, [loading, valuesForQuickstartContext]); + + const drawerProps = { + appendTo, + fullWidth, + onCloseInProgress, + onCloseNotInProgress, + ...props, + }; + + return ( + + {isManagedDrawer ? ( + {children} + ) : ( + children + )} + + ); +}; + +export default QuickStartContainer; diff --git a/packages/module/src/QuickStartDrawer.tsx b/packages/module/src/QuickStartDrawer.tsx index d793092c..47f2bca7 100644 --- a/packages/module/src/QuickStartDrawer.tsx +++ b/packages/module/src/QuickStartDrawer.tsx @@ -1,142 +1,15 @@ import * as React from 'react'; import { Drawer, DrawerContent, DrawerContentBody } from '@patternfly/react-core'; import QuickStartCloseModal from './QuickStartCloseModal'; -import QuickStartPanelContent from './QuickStartPanelContent'; +import QuickStartDrawerContent from './QuickStartDrawerContent'; import { getDefaultQuickStartState, QuickStartContext, QuickStartContextValues, - useValuesForQuickStartContext, } from './utils/quick-start-context'; import { QUICKSTART_ID_FILTER_KEY } from './utils/const'; -import { QuickStart, QuickStartStatus, AllQuickStartStates } from './utils/quick-start-types'; +import { QuickStart, QuickStartStatus } from './utils/quick-start-types'; import { getQuickStartByName } from './utils/quick-start-utils'; - -export interface QuickStartContainerProps extends React.HTMLProps { - /** array of quick starts */ - quickStarts: QuickStart[]; - /** id of the currently active quick start */ - activeQuickStartID: string; - /** setter for the active quick starts */ - setActiveQuickStartID: React.Dispatch>; - /** quick starts state object */ - allQuickStartStates: AllQuickStartStates; - /** setter for the quick starts state object */ - setAllQuickStartStates: React.Dispatch>; - /** content to render within the container */ - children?: React.ReactNode; - /** element to render the drawer panel into */ - appendTo?: HTMLElement | (() => HTMLElement); - /** if true, the panel will take up the full browser width */ - fullWidth?: boolean; - /** callback when an in-progress quick start is closed */ - onCloseInProgress?: any; - /** callback when a not-in-progress quick start is closed */ - onCloseNotInProgress?: any; - /** true to show footer buttons in the catalog tiles (default true) */ - showCardFooters?: boolean; - /** true to use legacy drawer header variant colors */ - useLegacyHeaderColors?: boolean; - /** text resources object */ - resourceBundle?: any; - /** language of the current resource bundle - * Add custom strings: https://github.com/patternfly/patternfly-quickstarts/tree/main/packages/module#localization - */ - language?: string; - /** if true, will show a loading spinner on the catalog page (default false) */ - loading?: boolean; - /** if true, will update the browser URL with ?quickstart={ID} (default true) */ - useQueryParams?: boolean; - /** if true, the TaskReview component, aka "Check your work", will be shown regardless of task status */ - alwaysShowTaskReview?: boolean; - /** - * Additional markdown extensions and renderers to use - * Example usage: https://github.com/patternfly/patternfly-quickstarts/tree/main/packages/module#markdown-extensions - */ - markdown?: { - extensions?: any[]; - renderExtension?: (docContext: Document, rootSelector: string) => React.ReactNode; - }; - /** additional quick start context props */ - contextProps?: QuickStartContextValues; -} - -export const QuickStartContainer: React.FC = ({ - quickStarts, - children, - activeQuickStartID, - allQuickStartStates, - setActiveQuickStartID, - setAllQuickStartStates, - appendTo, - fullWidth, - onCloseInProgress, - onCloseNotInProgress, - resourceBundle, - showCardFooters, - useLegacyHeaderColors, - language, - loading = false, - useQueryParams = true, - markdown, - contextProps, - alwaysShowTaskReview = true, - ...props -}: QuickStartContainerProps) => { - const valuesForQuickstartContext: QuickStartContextValues = useValuesForQuickStartContext({ - allQuickStarts: quickStarts, - activeQuickStartID, - setActiveQuickStartID, - allQuickStartStates, - setAllQuickStartStates, - footer: { - show: showCardFooters, - }, - useLegacyHeaderColors, - language, - resourceBundle: { - ...resourceBundle, - // Start: "Let's go!", - // Continue: 'Resume', - // Restart: 'Start over', - }, - loading, - useQueryParams, - markdown, - alwaysShowTaskReview, - ...contextProps, - }); - - React.useEffect(() => { - if ( - quickStarts && - JSON.stringify(quickStarts) !== JSON.stringify(valuesForQuickstartContext.allQuickStarts) - ) { - valuesForQuickstartContext.setAllQuickStarts(quickStarts); - } - }, [quickStarts, valuesForQuickstartContext]); - - React.useEffect(() => { - if (loading !== valuesForQuickstartContext.loading) { - valuesForQuickstartContext.setLoading(loading); - } - }, [loading, valuesForQuickstartContext]); - - const drawerProps = { - appendTo, - fullWidth, - onCloseInProgress, - onCloseNotInProgress, - ...props, - }; - - return ( - - {children} - - ); -}; - export interface QuickStartDrawerProps extends React.HTMLProps { quickStarts?: QuickStart[]; children?: React.ReactNode; @@ -159,7 +32,6 @@ export const QuickStartDrawer: React.FC = ({ activeQuickStartID, setActiveQuickStart, allQuickStarts = [], - activeQuickStartState, allQuickStartStates, setAllQuickStartStates, } = React.useContext(QuickStartContext); @@ -189,9 +61,8 @@ export const QuickStartDrawer: React.FC = ({ }, [activeQuickStartID, allQuickStartStates, setAllQuickStartStates]); const [modalOpen, setModalOpen] = React.useState(false); - const activeQuickStartStatus = activeQuickStartState?.status; const onClose = () => setActiveQuickStart(''); - const handleClose = () => { + const handleClose = (activeQuickStartStatus: string | number) => { if (activeQuickStartStatus === QuickStartStatus.IN_PROGRESS) { if (onCloseInProgress) { onCloseInProgress(); @@ -212,14 +83,6 @@ export const QuickStartDrawer: React.FC = ({ const onModalCancel = () => setModalOpen(false); - const fullWidthPanelStyle = fullWidth - ? { - style: { - flex: 1, - }, - } - : {}; - const fullWidthBodyStyle = fullWidth ? { style: { @@ -228,27 +91,22 @@ export const QuickStartDrawer: React.FC = ({ } : {}; - const panelContent = ( - - ); - return ( <> - {children ? ( - - {children} - - ) : ( -
{panelContent}
- )} + + } + {...fullWidthBodyStyle} + > + {children} +
= ({ ); }; + +export default QuickStartDrawer; diff --git a/packages/module/src/QuickStartDrawerContent.tsx b/packages/module/src/QuickStartDrawerContent.tsx new file mode 100644 index 00000000..7791f7ee --- /dev/null +++ b/packages/module/src/QuickStartDrawerContent.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +import QuickStartPanelContent from './QuickStartPanelContent'; +import { QuickStartContext, QuickStartContextValues } from './utils/quick-start-context'; +import { QuickStart } from './utils/quick-start-types'; + +export interface QuickStartDrawerContentProps extends React.HTMLProps { + quickStarts?: QuickStart[]; + appendTo?: HTMLElement | (() => HTMLElement); + fullWidth?: boolean; + onCloseInProgress?: any; + onCloseNotInProgress?: any; + handleDrawerClose?: (quickStartStatus: string | number) => void; +} + +export const QuickStartDrawerContent: React.FC = ({ + quickStarts = [], + appendTo, + fullWidth, + handleDrawerClose, + ...props +}) => { + const { + activeQuickStartID, + allQuickStarts = [], + activeQuickStartState, + } = React.useContext(QuickStartContext); + const combinedQuickStarts = allQuickStarts.concat(quickStarts); + + const handleClose = () => { + handleDrawerClose && handleDrawerClose(activeQuickStartState?.status); + }; + + const fullWidthPanelStyle = fullWidth + ? { + style: { + flex: 1, + }, + } + : {}; + + return ( + + ); +}; + +export default QuickStartDrawerContent; diff --git a/packages/module/src/index.ts b/packages/module/src/index.ts index 23f18d03..031184e7 100644 --- a/packages/module/src/index.ts +++ b/packages/module/src/index.ts @@ -3,7 +3,10 @@ import './styles/style.scss'; export * from './QuickStartCatalogPage'; export * from './catalog'; export * from './ConsoleInternal/components/utils'; +export * from './QuickStartContainer'; export * from './QuickStartDrawer'; +export * from './QuickStartDrawerContent'; +export * from './QuickStartCloseModal'; export * from './HelpTopicDrawer'; export * from './utils/const'; export * from './utils/quick-start-context';