@stack-spot/portal-layout
v2.20.0
Published
Implements the general layout for a Stackspot web application.
Downloads
2,464
Maintainers
Keywords
Readme
Layout
Implements the general layout for a Stackspot web application.
Components
- Header: a top bar with a logo and a user menu.
- Menu (sections): a left vertical bar with every section in the application.
- Menu (content, or contextual): a left vertical bar on the right of the menu sections that shows the menu for the current section or page.
- Page: the actual page content.
- Modal: a floating panel to show modal content.
- Right panel: a floating panel that shows like a modal, but is fixed to the right side of the window.
- Bottom dialog: a floating fixed panel fixed to the bottom of the window.
- Toaster: a floating set of cards at the top right corner used to show notifications.
- Tour: component associated floating cards used to present new features to users.
The overlay components (modal, right panel, bottom dialog and toaster) are controlled imperatively through the overlay object. Example:
overlay.showModal(options)
.
The header, menus and page are rendered by the component <Layout>
according to its props. If you need a custom header or menu, you can use
<RawLayout>
which accepts React elements instead of configuration objects.
You can also render the menu and header separately by importing the components <MenuContent>
, <MenuSections>
or <Header>
.
The Layout component
To render the layout, use the component <Layout>
, check the example below:
const menu = {
options: [
{
label: 'Section 1',
icon: <MyIcon />,
href: '/section-1',
active: true,
},
{
label: 'Section 2',
icon: <MyIcon2 />,
href: '/section-2',
}
]
}
const header = {
userName: 'Test',
email: '[email protected]',
center: <SearchField />,
options: [
{
label: 'Logout',
icon: <Logout />,
onClick: () => logout(),
}
]
}
const MyApp = () => {
return <Layout menu={menu} header={header}>Hello World!</Layout>
}
The Header and Menu in a layout are highly customizable. Check the code documentation for more details!
Modal and right panel pitfalls
Both these elements of the layout were designed to be used as imperative/procedural calls, i.e. instead of rendering a modal based on a
React State, we just call overlay.showModal(content)
or await confirm({ message: '...' })
. This makes using a modal much easier, but
prevents us from using React Portals.
Without the use of React Portals, the modal content becomes isolated from the context it was called, meaning, updating a state of the component who called it, won't update the content of the modal.
Programming a modal isolated from whatever called it is a good thing! It makes the code more maintainable and less coupled. But we need to keep this behavior in mind. See the example below:
const Counter = () => {
const [current, setCurrent] = useState(0)
function showCounterModal() {
overlay.showModal({
title: 'Counter',
children: (
<div>
<p>The value is {counter}.</p>
<button onClick={setCurrent(v => v + 1)}>Increment</button>
</div>
),
})
}
return <button onClick={showCounterModal}>Show counter modal</button>
}
This won't work because the state was declared outside the modal. We may click as muck as we want to increment the value, the text will keep saying the first value it showed. The value will only be updated once we close the modal and open it up again.
The correct way of doing this would be, instead of declaring the state outside the modal, declaring it inside, see the example below:
const CounterModal = () => {
const [current, setCurrent] = useState(0)
return (
<div>
<p>The value is {counter}.</p>
<button onClick={setCurrent(v => v + 1)}>Increment</button>
</div>
)
}
const Counter = () => {
function showCounterModal() {
overlay.showModal({
title: 'Counter',
children: <CounterModal />,
})
}
return <button onClick={showCounterModal}>Show counter modal</button>
}
This is going to work as expected, because the modal controls its own state!
If you really need to use an external state, consider:
- Using a React Context declared before
<Layout>
. - Calling
overlay.showModal()
again to force its UI to update with the new state value.
Attention: opening and closing modals are side-effects!
The operations to open or close a modal or right panel are side effects, and, because of this, they should not be called during the rendering phase of a component!
You should open/close a modal/rightPanel on events (e.g. onClick
) or React effects (e.g. useEffect
).
Layout elements
You can easily refer to layout elements by using their ids: elementIds
or by calling the function getLayoutElements()
.
Error handling
Every part of the layout is wrapped under a React Error Boundary. If an error occurs in a part of the layout that is large enough (e.g. Page), an error feedback is rendered. Otherwise, the content stays empty. The error is always logged to the console.
In development mode, a button shows up in the error feedback, the button allows the error message to be seen without opening the console. For the error boundaries that don't show an error feedback, in development mode, a small error icon is rendered.
To better format errors in an error boundary, please pass errorDescriptor
to the component <Layout>
, this is a function that transforms
an Error into something readable to present to the user.
To intercept every error when it's catch by an Error Boundary, use the property onError
of the component <Layout>
. This is useful for
sending error reports to the backend.