diff --git a/src/components/MethodSelector/MethodOptionContent.js b/src/components/MethodSelector/MethodOptionContent.js index 4d9afbb36..f9f659c69 100644 --- a/src/components/MethodSelector/MethodOptionContent.js +++ b/src/components/MethodSelector/MethodOptionContent.js @@ -2,18 +2,27 @@ import React from 'react'; import { css, cx } from '@leafygreen-ui/emotion'; import ComponentFactory from '../ComponentFactory'; import { theme } from '../../theme/docsTheme'; +import { isOfflineDocsBuild } from '../../utils/is-offline-docs-build'; +import { OFFLINE_CONTENT_CLASSNAME } from '../../utils/head-scripts/offline-ui/method-selector'; import MethodDescription from './MethodDescription'; const METHOD_DESCRIPTION_NAME = 'method-description'; const displayStyle = (isSelectedOption) => css` - ${!isSelectedOption && 'display: none;'} + ${!isSelectedOption && !isOfflineDocsBuild && 'display: none;'} `; export const getTestId = (optionId) => `method-option-content-${optionId}`; const containerStyle = css` margin-top: ${theme.size.default}; + + ${isOfflineDocsBuild && + ` + &[aria-expanded=false] { + display: none; + } + `} `; const MethodOptionContent = ({ @@ -27,7 +36,15 @@ const MethodOptionContent = ({ const methodDescription = children.find(({ name }) => name === METHOD_DESCRIPTION_NAME); return ( -
+
{methodDescription && } {children.map((node, index) => { if (node.name === METHOD_DESCRIPTION_NAME) return null; diff --git a/src/components/MethodSelector/MethodSelector.js b/src/components/MethodSelector/MethodSelector.js index 0cf78ce2d..6cd0c9e56 100644 --- a/src/components/MethodSelector/MethodSelector.js +++ b/src/components/MethodSelector/MethodSelector.js @@ -6,6 +6,8 @@ import { theme } from '../../theme/docsTheme'; import { getLocalValue, setLocalValue } from '../../utils/browser-storage'; import { reportAnalytics } from '../../utils/report-analytics'; import { ContentsContext } from '../Contents/contents-context'; +import { isOfflineDocsBuild } from '../../utils/is-offline-docs-build'; +import { OFFLINE_CLASSNAME } from '../../utils/head-scripts/offline-ui/method-selector'; import MethodOptionContent from './MethodOptionContent'; const STORAGE_KEY = 'methodSelectorId'; @@ -50,8 +52,22 @@ const radioBoxStyle = css` padding: 14px 12px; background-color: inherit; color: inherit; + + ${isOfflineDocsBuild && + ` + border-color: ${palette.gray.base}; + box-shadow: none; + `} } + ${isOfflineDocsBuild && + ` + &[aria-selected=true] div { + border-color: transparent; + box-shadow: 0 0 0 3px ${palette.green.dark1}; + } + `} + :not(:last-of-type) { margin: 0; } @@ -129,7 +145,7 @@ const MethodSelector = ({ nodeData: { children } }) => { return ( <> -
+
{ > {children.map(({ options: { title, id } }, index) => { return ( - + {title} ); })} {/* Keep separate div for triangle to allow for relative positioning */} -
-
-
-
+ {/* Offline docs will not have this triangle indicator */} + {!isOfflineDocsBuild && ( +
+
+
+
+ )}
{children.map((child, index) => { if (child.name !== 'method-option') return null; diff --git a/src/utils/head-scripts/offline-ui/index.js b/src/utils/head-scripts/offline-ui/index.js index 650026706..efcb46283 100644 --- a/src/utils/head-scripts/offline-ui/index.js +++ b/src/utils/head-scripts/offline-ui/index.js @@ -2,6 +2,8 @@ import bindTabUI from './tabs'; import bindCollapsibleUI from './collapsible'; import updateSidenavHeight from './sidenav'; import bindTabsSelectorsUI from './tabs-selectors'; +import bindMethodSelectorUI from './method-selector'; + const OFFLINE_UI_CLASSNAME = 'snooty-offline-ui'; const getScript = ({ key, fn }) => ( @@ -13,6 +15,10 @@ const getScript = ({ key, fn }) => ( /> ); -export const OFFLINE_HEAD_SCRIPTS = [bindTabUI, updateSidenavHeight, bindTabsSelectorsUI, bindCollapsibleUI].map( - (fn, idx) => getScript({ key: `offline-docs-ui-${idx}`, fn }) -); +export const OFFLINE_HEAD_SCRIPTS = [ + bindTabUI, + updateSidenavHeight, + bindTabsSelectorsUI, + bindCollapsibleUI, + bindMethodSelectorUI, +].map((fn, idx) => getScript({ key: `offline-docs-ui-${idx}`, fn })); diff --git a/src/utils/head-scripts/offline-ui/method-selector.js b/src/utils/head-scripts/offline-ui/method-selector.js new file mode 100644 index 000000000..8d7e41271 --- /dev/null +++ b/src/utils/head-scripts/offline-ui/method-selector.js @@ -0,0 +1,51 @@ +function bindMethodSelectorUI() { + const onContentLoaded = () => { + try { + // find all method selectors + const methodSelectorComponents = document.querySelectorAll('.offline-method-selector'); + for (const methodSelectorComponent of methodSelectorComponents) { + // find the button group role=group + const buttonGroup = methodSelectorComponent.querySelector('[role=group]'); + + // find the radio box inputs within button group and bind action + const buttons = buttonGroup?.querySelectorAll('input') ?? []; + + // find all the content within method selectors + const contentDivs = + methodSelectorComponent.parentElement?.querySelectorAll('.offline-method-selector-content') ?? []; + + // find all the labels to style for selected + const labels = methodSelectorComponent.querySelectorAll('label'); + + // for each input, find value `{name}-{index}` ie. `driver-0` + // find content div with data-testid=[method-option-content-{name}] and show that content div + for (let idx = 0; idx < buttons.length; idx++) { + const button = buttons[idx]; + button.addEventListener('click', (e) => { + const id = (e.currentTarget.getAttribute('value') || '').split('-')[0]; + const targetTestId = 'method-option-content-' + id; + const parentLabel = button.parentElement; + for (const contentDiv of contentDivs) { + contentDiv.setAttribute('aria-expanded', targetTestId === contentDiv.getAttribute('data-testid')); + } + for (const label of labels) { + label.setAttribute('aria-selected', label.isSameNode(parentLabel)); + } + }); + } + + // on load, set first label as active + labels[0]?.setAttribute('aria-selected', true); + } + } catch (e) { + console.error(e); + } + }; + + document.addEventListener('DOMContentLoaded', onContentLoaded, false); +} + +export default bindMethodSelectorUI; + +export const OFFLINE_CLASSNAME = `offline-method-selector`; +export const OFFLINE_CONTENT_CLASSNAME = `offline-method-selector-content`;