@stack-spot/portal-layout
TypeScript icon, indicating that this package has built-in type declarations

1.1.0 • Public • Published

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.

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: 'test@stackspot.com',
  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:

  1. Using a React Context declared before <Layout>.
  2. 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.

Readme

Keywords

none

Package Sidebar

Install

npm i @stack-spot/portal-layout

Weekly Downloads

204

Version

1.1.0

License

none

Unpacked Size

296 kB

Total Files

131

Last publish

Collaborators

  • larissacostazup
  • leonardo.palis
  • rafamsilva-zup
  • lucasgarciazup
  • hiltonpaulozup
  • magno.lemos
  • tiagobalbinodesazup
  • carolinegoncalveszup
  • renanrodrigueszup
  • tarcisiogc
  • arthurbleilzup
  • tiagoperesfr