Skip to content

Commit

Permalink
STCOM-1353 Export StripesOverlayWrapper and document its existence, a…
Browse files Browse the repository at this point in the history
…nd usage (#2360)

* export StripesOverlayWrapper and document its existence, and usage

* add portal problem/example to storybook

* typos

* update usePortal documentation for Autosuggest

* log changes
  • Loading branch information
JohnC-80 authored Oct 7, 2024
1 parent d7e8ae8 commit bd41e38
Show file tree
Hide file tree
Showing 14 changed files with 55 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const config = {
titlePrefix: 'Components',
files: '**/*.stories.[tj]s'
}],

staticDirs: ['../guides/static'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-mdx-gfm'
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
* Default the scope of `<CommandList>` to `document.body`. Remove internal wrapping div. Refs STCOM-1351.
* Remove inline styling from `<Modal>` content. Refs STCOM-1348.
* Use `<Selection>`'s formatter for rendering the selected value within the field and prevent overflow of text. Refs STCOM-1344.
* Export `<StripesOverlayWrapper>` that will automatically apply the `usePortal` behavior to nested overlay components. Refs STCOM-1353.

## [12.1.0](https://github.com/folio-org/stripes-components/tree/v12.1.0) (2024-03-12)
[Full Changelog](https://github.com/folio-org/stripes-components/compare/v12.0.0...v12.1.0)
Expand Down
28 changes: 28 additions & 0 deletions guides/UIModuleLayout.stories.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Meta } from '@storybook/addon-docs';
import usePortalFalse from './static/use-portal-false.gif';
import usePortalTrue from './static/use-portal-true.gif';

<Meta title="UI Layout" />

Expand Down Expand Up @@ -42,3 +44,29 @@ class LayoutExample extends React.Component {
export default LayoutExample;
```

## Portals

Another detail of the stripes UI is that we render a special element that 'overlay' components such as Dropdowns or Datepicker's Calendar control can render into if the controls run the risk of
being cut off by the border of a containing element. This image shows an example of this problem - controls in the list are cut off by the list's wrapping element.

<img style={{ border: '1px solid #AAA' }} src={usePortalFalse} alt="Problematic overlay control, cut off by the border of its container."/>

Each of these overlay components implements a `usePortal` prop that will render its menu to the special overlay element. We also export a `<StripesOverlayWrapper>` that will automatically apply
the `usePortal` behavior to nested overlay components. `<Modal>` and `<MultiColumnList>` components build in a `<StripesOverlayWrapper>` by default.

<img style={{ border: '1px solid #AAA' }} src={usePortalTrue} alt="Overlay control with issue resolve via the 'usePortal' prop." />

This feature should **only** be used when overlapping is possible since it does involve rendering interactive elements/sending focus to other parts of the DOM.

Basic Usage:

```
{/* usePortal is necessary for individual components outside of a StripesOverlayWrapper */}
<Datepicker usePortal />
<StripesOverlayWrapper>
{/* usePortal isn't necessary here */}
<ListOfOverlayComponents />
</StripesOverlayWrapper>
```
Binary file added guides/static/use-portal-false.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added guides/static/use-portal-true.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export {
currenciesByNumber,
currenciesOptions
} from './util/currencies';

export { default as StripesOverlayWrapper } from './util/StripesOverlayWrapper';
export {
default as countries,
countriesByCode,
Expand Down
4 changes: 1 addition & 3 deletions lib/AutoSuggest/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ prop | description | default | required
`onChange` | Callback called when the value changes | |
`renderOption` | Callback that renders the item in the dropdown | `item => item.value` |
`renderValue` | Callback that render the item in the input field | `item => item.value` |
`usePortal` | bool | If `true`, suggestion list will render to the `div[#OverlayContainer]` element in the FOLIO UI. | |
`usePortal` | bool | If `true`, option list will render to the `div[#OverlayContainer]` element in the FOLIO UI. Given the container of this component, `usePortal` may not be required. See [portals documentation](https://folio-org.github.io/stripes-components/iframe.html?viewMode=docs&id=guides-ui-layout--docs#portals) for guidance. | |
`valueKey` | The key in the item object to use as the value. | `"value"`
`withFinalForm` | toggle form time: true - final-form, false - redux-from (default) | |
`popper` | object | Used to adjust placement of options list overlay via underlying Popper component. [See `<Popper>` props](../Popper/readme.md) | |


2 changes: 1 addition & 1 deletion lib/Datepicker/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Name | type | description | default | required
`timeZone` | string | Overrides the time zone provided by context. | "UTC" | false
`useFocus` | bool | if set to false, component relies solely on clicking the calendar icon to toggle appearance of calendar. | true | false
`useInput` | bool | tells the Datepicker that it is being used under react-final-form, so it can modify its behaviour accodingly. **This is necessary when used with react-final-form** or the Datepicker will not work. | false | false
`usePortal` | bool | if true, the Datepicker will render itself to a React-Portal (the `#OverlayContainer` div) this avoids haveing the Datepicker cutoff by overflow. | false | false
`usePortal` | bool | if true, the Datepicker will render itself to a React-Portal (the `#OverlayContainer` div) this avoids having the Datepicker cut off by overflow. Given the container of this component, `usePortal` may not be required. See [portals documentation](https://folio-org.github.io/stripes-components/iframe.html?viewMode=docs&id=guides-ui-layout--docs#portals) for guidance. | false | false
`value` | string | date to be displayed in the textfield. In forms, this is supplied by the initialValues prop supplied to the form | "" | false

<!-- dateFormat | string | system formatting for date. [Moment.js formats](https://momentjs.com/docs/#/displaying/format/) are supported | "MM/DD/YYYY" | false-->
Expand Down
6 changes: 3 additions & 3 deletions lib/Modal/WrappingElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import PropTypes from 'prop-types';
import * as focusTrap from 'focus-trap';
import { listen } from '../../util/listen';
import StripesOverlayContext from '../../util/StripesOverlayContext';
import StripesOverlayWrapper from '../../util/StripesOverlayWrapper';
import { OVERLAY_CONTAINER_SELECTOR } from '../../util/consts';
import calloutCSS from '../Callout/Callout.css';
import overlayCSS from '../Popper/Popper.css';
Expand Down Expand Up @@ -155,9 +155,9 @@ const WrappingElement = forwardRef(({
ref={wrappingElementRef}
{...rest}
>
<StripesOverlayContext.Provider value={{ usePortal: true }}>
<StripesOverlayWrapper>
{children}
</StripesOverlayContext.Provider>
</StripesOverlayWrapper>
</WrappingElementComponent>
);
});
Expand Down
6 changes: 3 additions & 3 deletions lib/MultiColumnList/MCLRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import memoizeOne from 'memoize-one';

import Icon from '../Icon';
import EmptyMessage from '../EmptyMessage';
import StripesOverlayContext from '../../util/StripesOverlayContext';
import StripesOverlayWrapper from '../../util/StripesOverlayWrapper';
import { HotKeys } from '../HotKeys';
import SRStatus from '../SRStatus';
import css from './MCLRenderer.css';
Expand Down Expand Up @@ -1936,7 +1936,7 @@ class MCLRenderer extends React.Component {
: css.mclRowContainer;

return (
<StripesOverlayContext.Provider value={{ usePortal: true }}>
<StripesOverlayWrapper>
<HotKeys handlers={this.handlers} attach={this.shortcutsRef} noWrapper>
<div className={css.mclContainer} ref={this.shortcutsRef} style={this.getOuterElementStyle()}>
<SRStatus ref={this.status} />
Expand Down Expand Up @@ -2019,7 +2019,7 @@ class MCLRenderer extends React.Component {
}
</div>
</HotKeys>
</StripesOverlayContext.Provider>
</StripesOverlayWrapper>
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/MultiSelection/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Name | type | description | default | required
`onRemove` | func | Event handler specifically called when an item is removed from the selection. The removed item is passed to the handler. | |
`placeholder` | string | Rendered as a placeholder for the control when no value is present. | |
`showLoading` | bool | Should render loading indicator on the field | |
`usePortal` | bool | If `true`, option list will render to the `div[#OverlayContainer]` element in the FOLIO UI. | |
`usePortal` | bool | If `true`, option list will render to the `div[#OverlayContainer]` element in the FOLIO UI. Given the container of this component, `usePortal` may not be required. See [portals documentation](https://folio-org.github.io/stripes-components/iframe.html?viewMode=docs&id=guides-ui-layout--docs#portals) for guidance. | |
`value` | array | Array of selected objects. | |
`valueFormatter` | func | Render function that accepts an object with keys for the option. The function is called to display values in the selected values list. If the prop is missing, `formatter` will be used instead. | |
`ariaLabelledBy` | string | Used for applying an accessible label if no `label` prop is provided | |
Expand Down
2 changes: 1 addition & 1 deletion lib/Selection/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Name | type | description | default | required
`useValidStyle` | bool | if true, "success" styles will be applied to control if it contains a valid value `onBlur` (using redux-form validation.) | false |
`autoFocus` | bool | If this prop is `true`, control will automatically focus on mount | |
`popper` | object | Used to adjust placement of options list overlay via underlying Popper component. [See `<Popper>` props](../Popper/readme.md) | | false |
`usePortal` | bool | If `true`, option list will render to the `div[#OverlayContainer]` element in the FOLIO UI. | |
`usePortal` | bool | If `true`, option list will render to the `div[#OverlayContainer]` element in the FOLIO UI. Given the container of this component, `usePortal` may not be required. See [portals documentation](https://folio-org.github.io/stripes-components/iframe.html?viewMode=docs&id=guides-ui-layout--docs#portals) for guidance. | |

## Labeling
Like other form controls in stripes-components, `<Selection>` abides by standard conventions for labeling props if alternatives to `label` (visible label with the control) are required... `aria-label` and `aria-labelledby` are useful for this. See [Accessiblity for developers documentation](https://github.com/folio-org/stripes-components/blob/master/guides/AccessibilityDevPrimer.stories.mdx#labeling) for more details about which to choose.
Expand Down
2 changes: 1 addition & 1 deletion lib/Timepicker/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Name | type | description | default | required
`timeZone` | string | Overrides the time zone provided by context. | "UTC" | false
`timeFormat` | string | String to override default time format according to locale | | false
`useInput` | bool | If true - Outputs the value as it is displayed in the input. | false |
`usePortal` | bool | if true, the Timepicker will render itself to a React-Portal (the `#OverlayContainer` div) this avoids haveing the Timepicker cutoff by overflow. | false | false
`usePortal` | bool | if true, the Timepicker will render itself to a React-Portal (the `#OverlayContainer` div) this avoids having the Timepicker cut off by overflow. Given the container of this component, `usePortal` may not be required. See [portals documentation](https://folio-org.github.io/stripes-components/iframe.html?viewMode=docs&id=guides-ui-layout--docs#portals) for guidance. | false | false
`value` | string | time to be displayed in the visible input. | | false


Expand Down
13 changes: 13 additions & 0 deletions util/StripesOverlayWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import StripesOverlayContext from './StripesOverlayContext';

/** UI modules can import `StripesOverlayWrapper` to wrap component trees where
* usage of the `usePortal` prop will be prevalent among children.
<StripesOverlayWrapper>
<ListOfOverlayComponentsLikeDatepickerSelectionEtc />
</StripesOverlayWrapper>
*/
export default ({ children }) => (
<StripesOverlayContext.Provider value={{ usePortal: true }}>
{children}
</StripesOverlayContext.Provider>
);

0 comments on commit bd41e38

Please sign in to comment.