diff --git a/.changeset/few-spoons-hear.md b/.changeset/few-spoons-hear.md new file mode 100644 index 00000000000..e8972608801 --- /dev/null +++ b/.changeset/few-spoons-hear.md @@ -0,0 +1,5 @@ +--- +"@primer/react": patch +--- + +Add support for the dvh unit in `PageLayout` in order to correctly display pane contents on iOS devices diff --git a/examples/nextjs/package-lock.json b/examples/nextjs/package-lock.json index fc8d474ec0f..0ceefb1a74c 100644 --- a/examples/nextjs/package-lock.json +++ b/examples/nextjs/package-lock.json @@ -22,7 +22,7 @@ "dependencies": { "@github/combobox-nav": "^2.1.5", "@github/markdown-toolbar-element": "^2.1.0", - "@github/paste-markdown": "^1.3.1", + "@github/paste-markdown": "^1.4.0", "@primer/behaviors": "^1.1.1", "@primer/octicons-react": "^17.3.0", "@primer/primitives": "7.9.0", @@ -84,7 +84,7 @@ "@typescript-eslint/eslint-plugin": "4.31.2", "@typescript-eslint/parser": "4.33.0", "@whitespace/storybook-addon-html": "5.0.0", - "@wojtekmaj/enzyme-adapter-react-17": "0.6.3", + "@wojtekmaj/enzyme-adapter-react-17": "0.6.7", "babel-core": "7.0.0-bridge.0", "babel-loader": "^8.2.2", "babel-plugin-add-react-displayname": "0.0.5", @@ -1326,7 +1326,7 @@ "@changesets/cli": "2.17.0", "@github/combobox-nav": "^2.1.5", "@github/markdown-toolbar-element": "^2.1.0", - "@github/paste-markdown": "^1.3.1", + "@github/paste-markdown": "^1.4.0", "@github/prettier-config": "0.0.4", "@primer/behaviors": "^1.1.1", "@primer/octicons-react": "^17.3.0", @@ -1368,7 +1368,7 @@ "@typescript-eslint/eslint-plugin": "4.31.2", "@typescript-eslint/parser": "4.33.0", "@whitespace/storybook-addon-html": "5.0.0", - "@wojtekmaj/enzyme-adapter-react-17": "0.6.3", + "@wojtekmaj/enzyme-adapter-react-17": "0.6.7", "babel-core": "7.0.0-bridge.0", "babel-loader": "^8.2.2", "babel-plugin-add-react-displayname": "0.0.5", diff --git a/jest.config.js b/jest.config.js index 57f005b28b3..29e435deeb8 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,11 +3,8 @@ module.exports = { testEnvironment: 'jsdom', cacheDirectory: '.test', collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}', '!src/stories/**', '!**/*.stories.{js,jsx,ts,tsx}'], - setupFilesAfterEnv: [ - '/src/utils/test-matchers.tsx', - '/src/utils/test-deprecations.tsx', - '/src/utils/test-helpers.tsx' - ], + setupFiles: ['/src/utils/test-helpers.tsx'], + setupFilesAfterEnv: ['/src/utils/test-matchers.tsx', '/src/utils/test-deprecations.tsx'], testMatch: ['/(src|codemods)/**/*.test.[jt]s?(x)', '!**/*.types.test.[jt]s?(x)'], transformIgnorePatterns: [ 'node_modules/(?!@github/combobox-nav|@koddsson/textarea-caret|@github/markdown-toolbar-element)' diff --git a/src/PageLayout/useStickyPaneHeight.ts b/src/PageLayout/useStickyPaneHeight.ts index 5cbbdd78480..8c64c3153f9 100644 --- a/src/PageLayout/useStickyPaneHeight.ts +++ b/src/PageLayout/useStickyPaneHeight.ts @@ -1,5 +1,6 @@ import React from 'react' import {useInView} from 'react-intersection-observer' +import {canUseDOM} from '../utils/environment' /** * Calculates the height of the sticky pane such that it always @@ -9,7 +10,7 @@ export function useStickyPaneHeight() { const rootRef = React.useRef(null) // Default the height to the viewport height - const [height, setHeight] = React.useState('100vh') + const [height, setHeight] = React.useState(dvh(100)) const [offsetHeader, setOffsetHeader] = React.useState(0) // Create intersection observers to track the top and bottom of the content region @@ -47,7 +48,9 @@ export function useStickyPaneHeight() { // We need to account for this when calculating the offset. const overflowScroll = Math.max(window.scrollY + window.innerHeight - document.body.scrollHeight, 0) - calculatedHeight = `calc(100vh - (max(${topOffset}px, ${offsetHeaderWithUnits}) + ${bottomOffset}px - ${overflowScroll}px))` + calculatedHeight = `calc(${dvh( + 100 + )} - (max(${topOffset}px, ${offsetHeaderWithUnits}) + ${bottomOffset}px - ${overflowScroll}px))` } setHeight(calculatedHeight) @@ -131,3 +134,23 @@ function isScrollable(element: Element) { return hasScrollableContent && !isOverflowHidden } + +// TODO: there is currently an issue with dvh on Desktop Safari 15.6, 16.0. To +// work around it, we check to see if the device supports touch along with the +// dvh unit in order to target iPad. When the bug is addressed this check will +// no longer be needed +// +// @see https://bugs.webkit.org/show_bug.cgi?id=242758 +const supportsTouchCallout = canUseDOM ? CSS.supports('-webkit-touch-callout', 'none') : false +const supportsDVH = canUseDOM ? CSS.supports('max-height', '100dvh') && supportsTouchCallout : false + +/** + * Convert the given value to a dvh value, if supported, otherwise it falls back + * to vh + */ +function dvh(value: number): string { + if (supportsDVH) { + return `${value}dvh` + } + return `${value}vh` +} diff --git a/src/utils/test-helpers.tsx b/src/utils/test-helpers.tsx index 78eb646c8f9..8adc3f47f32 100644 --- a/src/utils/test-helpers.tsx +++ b/src/utils/test-helpers.tsx @@ -5,3 +5,10 @@ global.ResizeObserver = jest.fn().mockImplementation(() => { disconnect: jest.fn() } }) + +global.CSS = { + escape: jest.fn(), + supports: jest.fn().mockImplementation(() => { + return false + }) +}