Skip to content

Commit

Permalink
feat: add lockScrolling prop (#3045)
Browse files Browse the repository at this point in the history
This prop allows people to enable scrolling in components where it's
otherwise locked for better user experience. While locking scrolling was
a highly requested feature, we still don't want to assume that people
never wanted to be able to scroll, so we're adding this prop as a way to
return to the previous behavior. Later we will see whether anyone
actually used it and potentially remove it.
  • Loading branch information
silvenon committed Jul 22, 2021
1 parent 8b21705 commit 461139d
Show file tree
Hide file tree
Showing 24 changed files with 89 additions and 56 deletions.
21 changes: 11 additions & 10 deletions packages/orbit-components/src/Dialog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ After adding import into your project you can use it simply like:

Table below contains all types of the props available in Dialog component.

| Name | Type | Description |
| :---------------- | :---------------------- | :----------------------------------------------------------------------------------------------------- |
| dataTest | `string` | Optional prop for testing purposes. |
| renderInPortal | `boolean` | Optional prop, set it to `false` if you're rendering Dialog inside a custom portal, defaults to `true` |
| description | `React.Node` | Optional description of the main action that Dialog performs. |
| illustration | `React.Node` | Optional illustration of the Dialog. |
| **primaryAction** | `React.Node` | Primary and required action that user can do with the Dialog. |
| secondaryAction | `React.Node` | Optional, secondary action that user can perform - possibility to close the Dialog most of the time. |
| onClose | `() => void \| Promise` | The title of the Dialog - preferably the purpose of the main action. |
| **title** | `React.Node` | The title of the Dialog - preferably the purpose of the main action. |
| Name | Type | Default | Description |
| :---------------- | :---------------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------ |
| dataTest | `string` | | Optional prop for testing purposes. |
| renderInPortal | `boolean` | | Optional prop, set it to `false` if you're rendering Dialog inside a custom portal, defaults to `true` |
| description | `React.Node` | | Optional description of the main action that Dialog performs. |
| illustration | `React.Node` | | Optional illustration of the Dialog. |
| **primaryAction** | `React.Node` | | Primary and required action that user can do with the Dialog. |
| secondaryAction | `React.Node` | | Optional, secondary action that user can perform - possibility to close the Dialog most of the time. |
| lockScrolling | `boolean` | `true` | Whether to prevent scrolling of the rest of the page while Dialog is open. This is on by default to provide a better user experience. |
| onClose | `() => void \| Promise` | | The title of the Dialog - preferably the purpose of the main action. |
| **title** | `React.Node` | | The title of the Dialog - preferably the purpose of the main action. |
1 change: 1 addition & 0 deletions packages/orbit-components/src/Dialog/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface Props extends Common.Global {
readonly illustration?: React.ReactNode;
readonly primaryAction: React.ReactNode;
readonly secondaryAction?: React.ReactNode;
readonly lockScrolling?: boolean;
readonly onClose?: Common.Callback;
}

Expand Down
3 changes: 2 additions & 1 deletion packages/orbit-components/src/Dialog/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,10 @@ const Dialog = ({
onClose,
renderInPortal = true,
illustration,
lockScrolling = true,
}: Props): React.Node => {
const wrapperRef = React.useRef<HTMLElement | null>(null);
useLockScrolling(wrapperRef);
useLockScrolling(wrapperRef, lockScrolling);
const ref = React.useRef(null);
const theme = useTheme();
const transitionLength = React.useMemo(() => parseFloat(theme.orbit.durationFast) * 1000, [
Expand Down
1 change: 1 addition & 0 deletions packages/orbit-components/src/Dialog/index.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type Props = {|
+illustration?: React.Node,
+primaryAction: React.Node,
+secondaryAction?: React.Node,
+lockScrolling?: boolean,
+onClose?: () => void | Promise<any>,
...Globals,
|};
Expand Down
1 change: 1 addition & 0 deletions packages/orbit-components/src/Modal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Table below contains all types of the props available in the Modal component.
| Name | Type | Default | Description |
| :------------------ | :------------------------- | :--------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| children | `React.Node` | | The content of the Modal. [See Subcomponents](#subcomponents) |
| lockScrolling | `boolean` | `true` | Whether to prevent scrolling of the rest of the page while Modal is open. This is on by default to provide a better user experience. |
| scrollingElementRef | ref (object or function) | | The scrolling element, which depends on the viewport |
| dataTest | `string` | | Optional prop for testing purposes. |
| fixedFooter | `boolean` | `false` | If `true` the ModalFooter will be fixed to the bottom of window. |
Expand Down
1 change: 1 addition & 0 deletions packages/orbit-components/src/Modal/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Size = "extraSmall" | "small" | "normal" | "large" | "extraLarge";
export interface Props extends Common.Global {
readonly size?: Size;
readonly children: React.ReactNode;
readonly lockScrolling?: boolean;
readonly scrollingElementRef?: React.Ref<HTMLElement>;
readonly autoFocus?: boolean;
readonly onClose?: Common.Event<
Expand Down
3 changes: 2 additions & 1 deletion packages/orbit-components/src/Modal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ const Modal: React.AbstractComponent<Props, Instance> = React.forwardRef<Props,
hasCloseButton = true,
disableAnimation = false,
dataTest,
lockScrolling = true,
}: Props,
ref,
) => {
Expand Down Expand Up @@ -386,7 +387,7 @@ const Modal: React.AbstractComponent<Props, Instance> = React.forwardRef<Props,
[scrollingElementRef],
);

useLockScrolling(scrollingElement);
useLockScrolling(scrollingElement, lockScrolling);

const modalContentRef = React.useCallback(
node => {
Expand Down
1 change: 1 addition & 0 deletions packages/orbit-components/src/Modal/index.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export type Props = {|
+size?: Size,
+children: React.Node,
+autoFocus?: boolean,
+lockScrolling?: boolean,
+scrollingElementRef?:
| ((instance: HTMLElement | null) => void)
| {| current: HTMLElement | null |},
Expand Down
35 changes: 18 additions & 17 deletions packages/orbit-components/src/Popover/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,24 @@ After adding import into your project you can use it simply like:

Table below contains all types of the props available in the Popover component.

| Name | Type | Default | Description |
| :---------------- | :---------------------- | :------------------ | :----------------------------------------------------------------------------------------------------------- |
| actions | `React.Node` | | Actions to display in the Popover [See Functional specs](#functional-specs). |
| **content** | `React.Node` | | The content to display in the Popover. |
| **children** | `React.Node` | | The reference element where the Popover will appear. |
| dataTest | `string` | | Optional prop for testing purposes. |
| offset | [`offset`](#offset) | `{left: 0, top: 0}` | Optional prop to set position offset |
| fixed | `boolean` | | Changes position to fixed from absolute, good for use in sticky components. |
| noPadding | `boolean` | `true` | Adds or removes padding around popover's content. |
| opened | `boolean` | | Prop for programmatically controlling Popover inner state. If `opened` is present open triggers are ignored. |
| preferredAlign | [`enum`](#enum) | `"start"` | The preferred position to choose [See Functional specs](#functional-specs). |
| preferredPosition | [`enum`](#enum) | `"bottom"` | The preferred align to choose [See Functional specs](#functional-specs). |
| overlapped | `boolean` | `false` | If `true`, the content of Popover will overlap the trigger children. |
| renderInPortal | `boolean` | `true` | Optional prop, set it to `false` if you're rendering Popover inside a custom portal, defaults to `true` |
| width | `string` | | Width of popover, if not set the with is set to `auto`. |
| onClose | `() => void \| Promise` | | Function for handling onClose. |
| onOpen | `() => void \| Promise` | | Function for handling onOpen. |
| Name | Type | Default | Description |
| :---------------- | :---------------------- | :------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------- |
| actions | `React.Node` | | Actions to display in the Popover [See Functional specs](#functional-specs). |
| **content** | `React.Node` | | The content to display in the Popover. |
| **children** | `React.Node` | | The reference element where the Popover will appear. |
| dataTest | `string` | | Optional prop for testing purposes. |
| offset | [`offset`](#offset) | `{left: 0, top: 0}` | Optional prop to set position offset |
| fixed | `boolean` | | Changes position to fixed from absolute, good for use in sticky components. |
| noPadding | `boolean` | `true` | Adds or removes padding around popover's content. |
| opened | `boolean` | | Prop for programmatically controlling Popover inner state. If `opened` is present open triggers are ignored. |
| preferredAlign | [`enum`](#enum) | `"start"` | The preferred position to choose [See Functional specs](#functional-specs). |
| preferredPosition | [`enum`](#enum) | `"bottom"` | The preferred align to choose [See Functional specs](#functional-specs). |
| overlapped | `boolean` | `false` | If `true`, the content of Popover will overlap the trigger children. |
| lockScrolling | `boolean` | `true` | Whether to prevent scrolling of the rest of the page while Popover is open on mobile. This is on by default to provide a better user experience. |
| renderInPortal | `boolean` | `true` | Optional prop, set it to `false` if you're rendering Popover inside a custom portal, defaults to `true` |
| width | `string` | | Width of popover, if not set the with is set to `auto`. |
| onClose | `() => void \| Promise` | | Function for handling onClose. |
| onOpen | `() => void \| Promise` | | Function for handling onOpen. |

## enum

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,15 @@ const PopoverContentWrapper = ({
overlapped,
shown,
fixed,
lockScrolling = true,
actions,
}: Props): React.Node => {
const { isInsideModal } = React.useContext(ModalContext);
const { isLargeMobile, isTablet } = useMediaQuery();
const popover: {| current: React.ElementRef<*> |} = React.useRef(null);
const content: {| current: React.ElementRef<*> |} = React.useRef(null);
const scrollingElementRef = React.useRef<HTMLElement | null>(null);
useLockScrolling(scrollingElementRef, !isLargeMobile);
useLockScrolling(scrollingElementRef, lockScrolling && !isLargeMobile);
const intervalRef = React.useRef(null);
const position = calculatePopoverPosition(preferredPosition, preferredAlign);
const scrollableParent = React.useMemo(() => getScrollableParent(containerRef.current), [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type Props = {|
+overlapped?: boolean,
+shown: boolean,
+fixed?: boolean,
+lockScrolling?: boolean,
+actions?: React.Node,
+onClose: (ev: SyntheticEvent<HTMLElement>) => void,
|};
Expand Down
1 change: 1 addition & 0 deletions packages/orbit-components/src/Popover/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface Props extends Common.Global {
readonly fixed?: boolean;
readonly actions?: React.ReactNode;
readonly offset?: Offset;
readonly lockScrolling?: boolean;
readonly onOpen?: Common.Callback;
readonly renderInPortal?: boolean;
readonly onClose?: Common.Callback;
Expand Down
2 changes: 2 additions & 0 deletions packages/orbit-components/src/Popover/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const Popover = ({
onClose,
onOpen,
fixed,
lockScrolling,
actions,
renderInPortal = true,
}: Props): React.Node => {
Expand Down Expand Up @@ -136,6 +137,7 @@ const Popover = ({
noPadding={noPadding}
overlapped={overlapped}
fixed={fixed}
lockScrolling={lockScrolling}
actions={actions}
>
{content}
Expand Down
1 change: 1 addition & 0 deletions packages/orbit-components/src/Popover/index.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export type Props = {|
+fixed?: boolean,
+actions?: React.Node,
+offset?: Offset,
+lockScrolling?: boolean,
+renderInPortal?: boolean,
+onOpen?: () => void | Promise<any>,
+onClose?: () => void | Promise<any>,
Expand Down
Loading

0 comments on commit 461139d

Please sign in to comment.