Skip to content

Commit 224f794

Browse files
daKmoRtlouisse
authored andcommitted
feat(overlays): align Overlays API + add DynamicOverlay
Co-authored-by: Gerjan van Geest <Gerjan.van.Geest@ing.com> Co-authored-by: Thijs Louisse <Thijs.Louisse@ing.com>"
1 parent e666180 commit 224f794

32 files changed

+3018
-1617
lines changed

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"docs",
2727
"src",
2828
"stories",
29-
"test",
29+
"test-helpers",
3030
"translations",
3131
"*.js"
3232
],
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
if (typeof window.KeyboardEvent !== 'function') {
2+
// e.g. is IE and needs "polyfill"
3+
const event = KeyboardEvent.prototype;
4+
const descriptor = Object.getOwnPropertyDescriptor(event, 'key');
5+
if (descriptor) {
6+
const keys = {
7+
Win: 'Meta',
8+
Scroll: 'ScrollLock',
9+
Spacebar: ' ',
10+
11+
Down: 'ArrowDown',
12+
Left: 'ArrowLeft',
13+
Right: 'ArrowRight',
14+
Up: 'ArrowUp',
15+
16+
Del: 'Delete',
17+
Apps: 'ContextMenu',
18+
Esc: 'Escape',
19+
20+
Multiply: '*',
21+
Add: '+',
22+
Subtract: '-',
23+
Decimal: '.',
24+
Divide: '/',
25+
};
26+
Object.defineProperty(event, 'key', {
27+
// eslint-disable-next-line object-shorthand, func-names
28+
get: function() {
29+
const key = descriptor.get.call(this);
30+
31+
// eslint-disable-next-line no-prototype-builtins
32+
return keys.hasOwnProperty(key) ? keys[key] : key;
33+
},
34+
});
35+
}
36+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
if (typeof window.KeyboardEvent !== 'function') {
2+
// e.g. is IE and needs "polyfill"
3+
const KeyboardEvent = (event, _params) => {
4+
// current spec for it https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent
5+
const params = {
6+
bubbles: false,
7+
cancelable: false,
8+
view: document.defaultView,
9+
key: false,
10+
location: false,
11+
ctrlKey: false,
12+
shiftKey: false,
13+
altKey: false,
14+
metaKey: false,
15+
repeat: false,
16+
..._params,
17+
};
18+
const modifiersListArray = [];
19+
if (params.ctrlKey) {
20+
modifiersListArray.push('Control');
21+
}
22+
if (params.shiftKey) {
23+
modifiersListArray.push('Shift');
24+
}
25+
if (params.altKey) {
26+
modifiersListArray.push('Alt');
27+
}
28+
if (params.metaKey) {
29+
modifiersListArray.push('Meta');
30+
}
31+
32+
const ev = document.createEvent('KeyboardEvent');
33+
// IE Spec for it https://technet.microsoft.com/en-us/windows/ff975297(v=vs.60)
34+
ev.initKeyboardEvent(
35+
event,
36+
params.bubbles,
37+
params.cancelable,
38+
params.view,
39+
params.key,
40+
params.location,
41+
modifiersListArray.join(' '),
42+
params.repeat ? 1 : 0,
43+
params.locale,
44+
);
45+
return ev;
46+
};
47+
KeyboardEvent.prototype = window.Event.prototype;
48+
window.KeyboardEvent = KeyboardEvent;
49+
}

packages/overlays/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Manages their position on the screen relative to other elements, including other
88
## Features
99

1010
- [**Overlays Manager**](./docs/OverlaysManager.md), a global repository keeping track of all different types of overlays.
11-
- [**Overlays Occurrences**](./docs/OverlayOccurrences.md), outline of all possible occurrences of overlays. Divided into two main types:
11+
- [**Overlays System: Scope**](./docs/OverlaySystemScope.md), outline of all possible occurrences of overlays. Divided into two main types:
1212
- [**Global Overlay Controller**](./docs/GlobalOverlayController.md), controller for overlays relative to the viewport.
1313
- [**Local Overlay Controller**](./docs/LocalOverlayController.md), controller for overlays positioned next to invokers they are related to.
1414

packages/overlays/docs/GlobalOverlayController.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# GlobalOverlayController
22

3-
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.
4-
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.
3+
This is a base class for different global overlays (e.g. a dialog, see [Overlay System: Scope](./OverlaySystemScope.md) - the ones positioned relatively to the viewport).
4+
5+
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. But for implementation details check out [Overlay System: Implementation](./OverlaySystemImplementation.md).
56

67
All supported types of global overlays are described below.
78

packages/overlays/docs/LocalOverlayController.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
# LocalOverlayController
22

3-
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. For more information strictly about the positioning of the content element to the reference element (invoker), please refer to the [positioning documentation](./LocalOverlayPositioning.md)
4-
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.
3+
This is a base class for different local overlays (e.g. a [tooltip](../../tooltip/), see [Overlay System: Scope](./OverlaySystemScope.md) - the ones positioned next to invokers they are related to).
4+
5+
For more information strictly about the positioning of the content element to the reference element (invoker), please refer to the [positioning documentation](./LocalOverlayPositioning.md).
6+
7+
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. But for implementation details check out [Overlay System: Implementation](./OverlaySystemImplementation.md).
58

69
All supported types of local overlays are described below.
710

Lines changed: 73 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,142 +1,115 @@
11
# Overlay System: Implementation
22

3-
This document provides an outline of all possible occurrences of overlays found in applications in
4-
general and thus provided by Lion.
5-
For all concepts referred to in this document, please read [Overlay System Scope](./OverlaySystemScope.md).
3+
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).
64

7-
## Local and global overlay controllers
8-
9-
Currently, we have a global and a local overlay controller, as two separate entities.
10-
Based on provided config, they handle all positioning logic, accessibility and interaction patterns.
11-
All of their configuration options will be described below as part of the _Responsive overlay_ section.
5+
## Base controller
126

13-
### Connection points and placement contexts
7+
The BaseController handles the basics of all controllers, and has the following public functions:
148

15-
It's currently not clear where the border between global and local overlays lie. They seem to be
16-
separated based on their 'dom connection point' (body vs 'page cursor'(usually invoker sibling)).
17-
However, there is no required relationship here: we can create a modal dialog from
18-
local context('page cursor') as well.
9+
- **show()**, to show the overlay.
10+
- **hide()**, to hide the overlay.
11+
- **toggle()**, to toggle between show and hide.
1912

20-
Only, we would have a few concerns when creating global overlays from a local connection point:
13+
All overlays exists of an invoker and a content
2114

22-
- Accessibility will be harder to implement. When wai-aria 1.0 needs to be supported, all siblings
23-
need to have aria-hidden="true" and all parents role="presentation". Not always straightforward
24-
in shadow dom. If we only need to support wai-aria 1.1, we could use aria-modal="true" on the
25-
element with role="dialog". (we basically need to test our supported browsers and screen readers
26-
for compatibility with aria-modal).
27-
- Stacking context need to be managed: the whole 'z-index chain' should win (it's a battle between
28-
parents in the hierarchy). This would require some complex code to cover all edge cases.
29-
- Side effects of parents adding transforms or clipping become a risk. This is hard to detect and
30-
'counter'.
15+
- **invoker**, the element that can trigger showing (and hiding) the overlay.
16+
- invokerNode
17+
- **content**, the toggleable overlays content
18+
- contentTemplate, in most cases the content will be placed inside a template as one of the controller configuration options.
19+
- contentNode, a node can also be used as the content for local overlays (see next section), such as is done in the [popup](../../popup/).
3120

32-
When the dom connection point is 'body', content projection will not work, but a template that
33-
can be rendered without being dependent on its context will be required.
21+
## Local and global overlay controllers
3422

35-
There usually also is a correllation with their relative positioning context: invoker/other
36-
relative element for local(tooltip/popover/dropdown) vs. 'window/viewport level' for global
37-
(dialog/toast/sheet).
23+
Currently, we have a global and a local overlay controller, as two separate entities.
24+
Based on provided config, they handle all positioning logic, accessibility and interaction patterns.
3825

39-
For responsive overlays (see below for an elaborate explanation), we need to switch from global to
40-
local. When we switch the dom connection point, (think of rotating a mobile or tablet), we
41-
will loose the current focus, which can be an a11y concern. This can eventually be 'catched' by
42-
syncing the activeElement (we would only loose the screenreader active element (for instance,
43-
focused cell in table mode))
26+
- [GlobalOverlayController](./GlobalOverlayController.md), the ones positioned relatively to the viewport.
27+
- [LocalOverlayController](./LocalOverlayController.md), the ones positioned next to invokers they are related to.
4428

45-
For maximum flexibility, it should be up to the developer to decide how overlays should be rendered,
46-
per instance of an overlay.
29+
All of their configuration options will be described below as part of the _Configuration options_ section.
4730

48-
### Responsive overlay
31+
### DynamicOverlayController
4932

5033
Based on screen size, we might want to switch the appearance of an overlay.
5134
For instance: an application menu can be displayed as a dropdown on desktop,
5235
but as a bottom sheet on mobile.
53-
Similarly, a dialog can be displayed as a popover on desktop, but as a (global) dialog on mobile.
5436

55-
To implement such a flexible overlay, we need an 'umbrella' layer that allows for switching between
56-
different configuration options, also between the connection point in dom (global and local).
37+
Similarly, a dialog can be displayed as a popover on desktop, but as a (global) dialog on mobile.
5738

58-
Luckily, interfaces of Global and OverlayControllers are very similar.
59-
Therefore we can make a wrapping ResponsiveOverlayController.
39+
The DynamicOverlayController is a flexible overlay that can switch between different controllers, also between the connection point in dom (global and local). The switch is only done when the overlay is closed, so the focus isn't lost while switching from one overlay to another.
6040

61-
### Configuration options for local and global overlays
41+
### Configuration options
6242

6343
In total, we should end up with configuration options as depicted below, for all possible overlays.
6444
All boolean flags default to 'false'.
65-
Some options are mutually exclusive, in which case their dependent options and requirement will be
66-
mentioned.
67-
Note: a more generic and precise term for all mentionings of `invoker` below would actually be
68-
`relative positioning element`.
45+
Some options are mutually exclusive, in which case their dependent options and requirement will be mentioned.
46+
47+
> Note: a more generic and precise term for all mentionings of `invoker` below would actually be `relative positioning element`.
48+
49+
#### Shared configuration options
6950

7051
```text
71-
- {Element} elementToFocusAfterHide - the element that should be called `.focus()` on after dialog closes
72-
- {Boolean} hasBackdrop - whether it should have a backdrop (currently exclusive to globalOverlayController)
73-
- {Boolean} isBlocking - hides other overlays when mutiple are opened (currently exclusive to globalOverlayController)
74-
- {Boolean} preventsScroll - prevents scrolling body content when overlay opened (currently exclusive to globalOverlayController)
75-
- {Boolean} trapsKeyboardFocus - rotates tab, implicitly set when 'isModal'
76-
- {Boolean} hidesOnEsc - hides the overlay when pressing [esc]
77-
- {Boolean} hidesOnOutsideClick - hides the overlay when clicking next to it, exluding invoker. (currently exclusive to localOverlayController)
78-
- {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
79-
- {TemplateResult} contentTemplate
80-
- {TemplateResult} invokerTemplate (currently exclusive to LocalOverlayController)
81-
- {Element} invokerNode (currently exclusive to LocalOverlayController)
82-
- {Element} contentNode (currently exclusive to LocalOverlayController)
52+
- {Boolean} trapsKeyboardFocus - rotates tab, implicitly set when 'isModal'.
53+
- {Boolean} hidesOnEsc - hides the overlay when pressing [esc].
8354
```
8455

85-
These options are suggested to be added to the current ones:
56+
#### Global specific configuration options
8657

8758
```text
88-
- {Boolean} isModal - sets aria-modal and/or aria-hidden="true" on siblings
89-
- {Boolean} isGlobal - determines the connection point in DOM (body vs handled by user) TODO: rename to renderToBody?
90-
- {Boolean} isTooltip - has a totally different interaction - and accessibility pattern from all
91-
other overlays, so needed for internals.
59+
- {Element} elementToFocusAfterHide - the element that should be called `.focus()` on after dialog closes.
60+
- {Boolean} hasBackdrop - whether it should have a backdrop.
61+
- {Boolean} isBlocking - hides other overlays when multiple are opened.
62+
- {Boolean} preventsScroll - prevents scrolling body content when overlay opened.
63+
- {Object} viewportConfig
64+
- {String} placement: 'top-left' | 'top' | 'top-right' | 'right' | 'bottom-left' |'bottom' |'bottom-right' |'left' | 'center'
65+
```
66+
67+
#### Local specific configuration options
68+
69+
```text
70+
- {Boolean} hidesOnOutsideClick - hides the overlay when clicking next to it, excluding invoker.
71+
- {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>.
72+
- For positioning checkout [localOverlayPositioning](./localOverlayPositioning.md).
73+
```
74+
75+
#### Suggested additions
76+
77+
```text
78+
- {Boolean} isModal - sets [aria-modal] and/or [aria-hidden="true"] on siblings
79+
- {Boolean} isTooltip - has a totally different interaction - and accessibility pattern from all other overlays, so needed for internals.
9280
- {Boolean} handlesUserInteraction - sets toggle on click, or hover when `isTooltip`
9381
- {Boolean} handlesAccessibility -
9482
- For non `isTooltip`:
95-
- sets aria-expanded="true/false" and aria-haspopup="true" on invokerNode
96-
- sets aria-controls on invokerNode
83+
- sets [aria-expanded="true/false"] and [aria-haspopup="true"] on invokerNode
84+
- sets [aria-controls] on invokerNode
9785
- returns focus to invokerNode on hide
9886
- sets focus to overlay content(?)
9987
- For `isTooltip`:
100-
- sets role="tooltip" and aria-labelledby/aria-describedby on the content
101-
- {Object} popperConfig
102-
- {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 '-'.
103-
- 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 `<select>` on iOS))
104-
- secondary : 'start' | 'end' | 'fill' (occupies width of invoker) | 'middle' (implicit option that will be choosen by default when none of the previous are specified)
105-
- all other configuration as described by [Popper.js](https://popper.js.org/)
88+
- sets [role="tooltip"] and [aria-labelledby]/[aria-describedby] on the content
10689
```
10790

108-
What we should think about more properly is a global placement option (positioned relative to window instead of invoker)
109-
110-
```text
111-
// TODO: namings very much under construction (we should also reconsider 'placement' names, see: https://github.com/ing-bank/lion/pull/61)
112-
// Something like the drawing Joren made: https://github.com/ing-bank/lion/issues/36#issuecomment-491855381
113-
- {String} viewportPlacement - consists of a vertical alignment part and an horizontal alignment part,
114-
separated via '-'
115-
- vertical align : 'center' | 'bottom' | 'top' | 'left' | 'right' | 'fullheight'
116-
- horizontal align: 'middle' | 'start' | 'end' | 'fullwidth'
117-
Examples: 'center-middle' (dialog, alertdialog), 'top-fullwidth' (top sheet)
118-
```
119-
120-
## Controllers/behaviors
91+
## Specific Controllers
12192

12293
Controllers/behaviors provide preconfigured configuration objects for the global/local
12394
overlay controllers.
95+
12496
They provide an imperative and very flexible api for creating overlays and should be used by
12597
Subclassers, inside webcomponents.
12698

12799
### Dialog Controller
128100

129101
```js
130102
{
131-
isGlobal: true,
132103
isModal: true,
133104
hasBackdrop: true,
134105
preventsScroll: true,
135106
trapsKeyboardFocus: true,
136107
hidesOnEsc: true,
137108
handlesUserInteraction: true,
138109
handlesAccessibility: true,
139-
viewportPlacement: 'center-middle',
110+
viewportConfig: {
111+
placement: 'center',
112+
},
140113
}
141114
```
142115

@@ -181,17 +154,18 @@ TODO:
181154

182155
```js
183156
{
184-
...Dialog,
185-
viewportPlacement: 'top-right', (?)
186-
}
157+
viewportconfig: {
158+
placement: 'top-right',
159+
},
187160
```
188161
189-
### Sheet Controller (bottom, top, left, right)
162+
### Bottomsheet Controller
190163
191164
```js
192165
{
193-
...Dialog,
194-
viewportPlacement: '{top|bottom|left|right}-fullwidth', (?)
166+
viewportConfig: {
167+
placement: 'bottom',
168+
},
195169
}
196170
```
197171
@@ -212,11 +186,9 @@ config based on media query from Dropdown to BottomSheet/CenteredDialog
212186
213187
## Web components
214188
215-
Web components provide a declaritive, developer friendly interface with a prewconfigured styling
216-
that fits the Design System and makes it really easy for Application Developers to build
217-
user interfaces.
218-
Web components should use
219-
The ground layers for the webcomponents in Lion are the following:
189+
Web components provide a declarative, developer friendly interface with a preconfigured styling that fits the Design System and makes it really easy for Application Developers to build user interfaces.
190+
191+
Web components should use the ground layers for the webcomponents in Lion are the following:
220192
221193
### Dialog Component
222194
@@ -242,7 +214,7 @@ Imperative might be better here? We can add a web component later if needed.
242214
243215
### Dropdown Component
244216
245-
Like the name suggests, the default placement will be button
217+
Like the name suggests, the default placement will be bottom
246218
247219
```html
248220
<lion-dropdown>
@@ -267,6 +239,6 @@ Imperative might be better here?
267239
268240
### Select, Combobox/autocomplete, Application menu
269241
270-
Those will be separate web components with a lot of form and a11y logic that will be described
271-
in detail in different sections.
272-
They will imoplement the Overlay configuration as described above under 'Controllers/behaviors'.
242+
Those will be separate web components with a lot of form and a11y logic that will be described in detail in different sections.
243+
244+
They will implement the Overlay configuration as described above under 'Controllers/behaviors'.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Overlay Manager
2+
3+
An overlay manager is a global repository keeping track of all different types of overlays. The need for a global housekeeping mainly arises when multiple overlays are opened simultaneously.
4+
5+
The overlay manager keeps track of all registered overlays and controls which one to show.

0 commit comments

Comments
 (0)