diff --git a/packages/overlays/README.md b/packages/overlays/README.md index da261a3fa..c39d236eb 100644 --- a/packages/overlays/README.md +++ b/packages/overlays/README.md @@ -5,16 +5,17 @@ Supports different types of overlays like dialogs, toasts, tooltips, dropdown, etc... Manages their position on the screen relative to other elements, including other overlays. -## Overlays manager - -This is a global singleton needed to manage positions of multiple dialogs next to each other on the entire page. -It does the job automatically, but you need to add every newly created overlay to it using the code provided below: +## Features +- [**Overlays Manager**](./docs/OverlaysManager.md), a global repository keeping track of all different types of overlays. +- [**Overlays Occurrences**](./docs/OverlayOccurrences.md), outline of all possible occurrences of overlays. Divided into two main types: + - [**Global Overlay Controller**](./docs/GlobalOverlayController.md), controller for overlays relative to the viewport. + - [**Local Overlay Controller**](./docs/LocalOverlayController.md), controller for overlays positioned next to invokers they are related to. ## How to use ### Installation ```sh -npm i --save @lion/ajax +npm i --save @lion/overlays ``` ### Example @@ -29,28 +30,3 @@ const myCtrl = overlays.add( // name OverlayTypeController is for illustration purpose only // please read below about existing classes for different types of overlays ``` - -## GlobalOverlayController - -This is a base class for different global overlays (e.g. a dialog) - the ones positioned relatively to the viewport. -You should not use this controller directly unless you want to create a unique type of global overlays which is not supported out of the box. - -All supported types of global overlays are described below. - -### ModalDialogController - -```js -import { ModalDialogController } from '@lion/overlays'; -``` - -This is an extension of GlobalOverlayController configured to create accessible modal dialogs. - -## LocalOverlayController - -This is a base class for different local overlays (e.g. a tooltip) - the ones positioned next to invokers they are related to. -You should not use this controller directly unless you want to create a unique type of local overlays which is not supported out of the box. - -All supported types of local overlays are described below. - -This is currently WIP. -Stay tuned for updates on new types of overlays. diff --git a/packages/overlays/docs/GlobalOverlayController.md b/packages/overlays/docs/GlobalOverlayController.md new file mode 100644 index 000000000..983acce33 --- /dev/null +++ b/packages/overlays/docs/GlobalOverlayController.md @@ -0,0 +1,31 @@ +# GlobalOverlayController + +This is a base class for different global overlays (e.g. a dialog, see [Overlay Occurrences](./OverlayOccurrences.md) - the ones positioned relatively to the viewport. +You should not use this controller directly unless you want to create a unique type of global overlays which is not supported out of the box. + +All supported types of global overlays are described below. + +## How to use + +### Installation +```sh +npm i --save @lion/overlays +``` + +### Example +```js +import { overlays } from '@lion/overlays'; + +const myCtrl = overlays.add( + new GlobalOverlayController({ + /* options */ + }) +); +``` + +### ModalDialogController +A specific extension of GlobalOverlayController configured to create accessible modal dialogs. + +```js +import { ModalDialogController } from '@lion/overlays'; +``` diff --git a/packages/overlays/docs/LocalOverlayController.md b/packages/overlays/docs/LocalOverlayController.md new file mode 100644 index 000000000..af097cfd7 --- /dev/null +++ b/packages/overlays/docs/LocalOverlayController.md @@ -0,0 +1,28 @@ +# LocalOverlayController + +This is a base class for different local overlays (e.g. a [tooltip](../../tooltip/), see [Overlay System Implementation](./OverlaySystemImplementation.md) - the ones positioned next to invokers they are related to. +You should not use this controller directly unless you want to create a unique type of local overlays which is not supported out of the box. + +All supported types of local overlays are described below. + +## How to use + +### Installation +```sh +npm i --save @lion/overlays +``` + +### Example +```js +import { overlays } from '@lion/overlays'; + +const myCtrl = overlays.add( + new LocalOverlayController({ + /* options */ + }) +); +``` + + +This is currently WIP. +Stay tuned for updates on new types of overlays. diff --git a/packages/overlays/docs/OverlaySystemImplementation.md b/packages/overlays/docs/OverlaySystemImplementation.md new file mode 100644 index 000000000..712fca38f --- /dev/null +++ b/packages/overlays/docs/OverlaySystemImplementation.md @@ -0,0 +1,240 @@ +# Overlay System: Implementation +This document provides an outline of all possible occurrences of overlays found in applications in +general and thus provided by Lion. +For all concepts referred to in this document, please read [Overlay System Scope](./OverlaySystemScope.md). + +## Local and global overlay controllers +Currently, we have a global and a local overlay controller, as two separate entities. +Based on provided config, they handle all positioning logic, accessibility and interaction patterns. +All of their configuration options will be described below as part of the _Responsive overlay_ section. + +### Connection points and placement contexts +It's currently not clear where the border between global and local overlays lie. They seem to be +separated based on their 'dom connection point' (body vs 'page cursor'(usually invoker sibling)). +However, there is no required relationship here: we can create a modal dialog from +local context('page cursor') as well. + +Only, we would have a few concerns when creating global overlays from a local connection point: +- Accessibility will be harder to implement. When wai-aria 1.0 needs to be supported, all siblings +need to have aria-hidden="true" and all parents role="presentation". Not always straightforward +in shadow dom. If we only need to support wai-aria 1.1, we could use aria-modal="true" on the +element with role="dialog". (we basically need to test our supported browsers and screen readers +for compatibility with aria-modal). +- Stacking context need to be managed: the whole 'z-index chain' should win (it's a battle between +parents in the hierarchy). This would require some complex code to cover all edge cases. +- Side effects of parents adding transforms or clipping become a risk. This is hard to detect and +'counter'. + +When the dom connection point is 'body', content projection will not work, but a template that +can be rendered without being dependent on its context will be required. + +There usually also is a correllation with their relative positioning context: invoker/other +relative element for local(tooltip/popover/dropdown) vs. 'window/viewport level' for global +(dialog/toast/sheet). + +For responsive overlays (see below for an elaborate explanation), we need to switch from global to +local. When we switch the dom connection point, (think of rotating a mobile or tablet), we +will loose the current focus, which can be an a11y concern. This can eventually be 'catched' by +syncing the activeElement (we would only loose the screenreader active element (for instance, +focused cell in table mode)) + +For maximum flexibility, it should be up to the developer to decide how overlays should be rendered, +per instance of an overlay. + +## Responsive overlay +Based on screen size, we might want to switch the appearance of an overlay. +For instance: an application menu can be displayed as a dropdown on desktop, +but as a bottom sheet on mobile. +Similarly, a dialog can be displayed as a popover on desktop, but as a (global) dialog on mobile. + +To implement such a flexible overlay, we need an 'umbrella' layer that allows for switching between +different configuration options, also between the connection point in dom (global and local). + +Luckily, interfaces of Global and OverlayControllers are very similar. +Therefore we can make a wrapping ResponsiveOverlayController. + +### Configuration options for local and global overlays + +In total, we should end up with configuration options as depicted below, for all possible overlays. +All boolean flags default to 'false'. +Some options are mutually exclusive, in which case their dependent options and requirement will be +mentioned. +Note: a more generic and precise term for all mentionings of `invoker` below would actually be +`relative positioning element`. +``` +- {Element} elementToFocusAfterHide - the element that should be called `.focus()` on after dialog closes +- {Boolean} hasBackdrop - whether it should have a backdrop (currently exclusive to globalOverlayController) +- {Boolean} isBlocking - hides other overlays when mutiple are opened (currently exclusive to globalOverlayController) +- {Boolean} preventsScroll - prevents scrolling body content when overlay opened (currently exclusive to globalOverlayController) +- {Boolean} trapsKeyboardFocus - rotates tab, implicitly set when 'isModal' +- {Boolean} hidesOnEsc - hides the overlay when pressing [esc] +- {Boolean} hidesOnOutsideClick - hides the overlay when clicking next to it, exluding invoker. (currently exclusive to localOverlayController) +- {String} cssPosition - 'absolute' or 'fixed'. TODO: choose name that cannot be mistaken for placement like cssPosition or positioningTechnique: https://github.com/ing-bank/lion/pull/61 +- {TemplateResult} contentTemplate +- {TemplateResult} invokerTemplate (currently exclusive to LocalOverlayController) +- {Element} invokerNode (currently exclusive to LocalOverlayController) +- {Element} contentNode (currently exclusive to LocalOverlayController) +``` + +These options are suggested to be added to the current ones: +``` +- {Boolean} isModal - sets aria-modal and/or aria-hidden="true" on siblings +- {Boolean} isGlobal - determines the connection point in DOM (body vs handled by user) TODO: rename to renderToBody? +- {Boolean} isTooltip - has a totally different interaction - and accessibility pattern from all +other overlays, so needed for internals. +- {Boolean} handlesUserInteraction - sets toggle on click, or hover when `isTooltip` +- {Boolean} handlesAccessibility - + - For non `isTooltip`: + - sets aria-expanded="true/false" and aria-haspopup="true" on invokerNode + - sets aria-controls on invokerNode + - returns focus to invokerNode on hide + - sets focus to overlay content(?) + - For `isTooltip`: + - sets role="tooltip" and aria-labelledby/aria-describedby on the content +- {Object} placementConfig + - {String} placement - vertical/horizontal position to be supplied to `managePosition`. See https://github.com/ing-bank/lion/pull/61 for current api. Consists of a primary part (where the overlay is located relative from invoker) and secondary alignnment part (how the overlay 'snaps' to the perpendicular boundary of the invoker), separated via '-'. + - primary : 'bottom' | 'top' | 'left' | 'right' | 'over' (this means the overlay will be positioned on top of the invoker. Think for instance of a select dropdown that opens a selected option on top of the invoker (default behavior of `