\n \n \n <>{children}>\n \n \n );\n}\n\nexport default VisuallyHidden;\n","import React, { forwardRef } from 'react';\nimport { t } from '@sm/intl';\nimport { uniqueId } from '@wds/utils';\nimport { useSurveyTheme } from '@sm/webassets';\nimport useStyles from './useStyles';\nimport { ProgressBar } from '~app/components/core/ProgressBar';\nimport { Theme } from '~app/sharedTypes';\nimport VisuallyHidden from '~app/components/core/VisuallyHidden';\n\n// TODO: Add back defineMessages() when ready to get translated\nconst COPY = {\n ANSWERED: {\n id: 'PersistentProgressBar.Answered',\n defaultMessage: '{value} of {total} answered',\n description: '[Type: Label][Vis: med] - Shows the total number of questions answered',\n },\n PROGRESS_SR_PREFIX: {\n id: 'PersistentProgressBar.Progress_Sr_Prefix',\n defaultMessage: 'Current Progress,',\n description: '[Type: Label][Vis: med] - Speech readout prefix denoting question progress',\n },\n};\n\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface Props {\n id?: string;\n /** The current value */\n value: number;\n /** The current value in percentage */\n total: number;\n /** Prop to set Right-to-Left */\n isRTL?: boolean;\n /** Used to override the styling */\n className?: string;\n}\n\n// No need for use memo since the only time the re-rendering happens is when the value change or theme\n// Also very light calculation\nconst getCurrentValue = (value: number, total: number): number => {\n // Avoid division by 0\n if (total <= 0) {\n return 0;\n }\n // Return the percentage\n return (value / total) * 100;\n};\n\nfunction PersistentProgressBar(\n { id = 'persistent-progress', value, total, isRTL }: Props,\n ref: React.Ref\n): React.ReactElement {\n // TODO: Remove any after the type updates are installed\n const theme: Theme = useSurveyTheme();\n const { progressBarContainer, progressBar, text } = useStyles({ isRTL: !!isRTL });\n const boundedCurrentValue = getCurrentValue(value, total);\n const progressId = uniqueId(`${id}-`);\n const progressLabelId = `${progressId}-label`;\n const progressBarId = `${progressId}-bar`;\n return (\n
\n \n {t(COPY.PROGRESS_SR_PREFIX)}\n {t(COPY.ANSWERED, { value, total })}\n \n \n
\n );\n}\n\nexport default forwardRef(PersistentProgressBar);\n","enum SurveyFormat {\n CLASSIC = 'CLASSIC',\n OQAAT = 'ONE_QUESTION_AT_A_TIME',\n CONVERSATIONAL = 'CONVERSATIONAL',\n}\n\nexport default SurveyFormat;\n","import SurveyFormat from './surveyFormatEnum';\n\nexport const extrapolateSurveyData = survey => {\n if (Object.entries(survey).length === 0) {\n return {};\n }\n\n return {\n isAutoScroll: survey.format === SurveyFormat.OQAAT,\n isFooterEnabled: survey.design.footer.enabled,\n isLogoEnabled: survey.design.logo.enabled,\n isQuestionNumberingEnabled: survey.design.questionNumbering.enabled,\n isSurveyTitleEnabled: survey.design.surveyTitle.enabled,\n langCode: survey.language?.code,\n logoProps: survey.design.logo,\n questions: survey.questionsQM?.items ?? [],\n showPageTitles: survey.design.showPageTitles,\n showRequiredQuestionAsterisks: survey.design.showRequiredQuestionAsterisks,\n surveyPages: survey.pages?.items || [],\n surveyTitle: survey.title,\n surveyTitleAlignment: survey.design.surveyTitle.horizontalAlignment,\n surveyTitleHTML: survey.titleHTML,\n themeVersion: survey?.design.theme.version,\n totalQuestionsCount: survey?.questionCount,\n };\n};\n\nexport default extrapolateSurveyData;\n","import { Styles } from 'react-jss';\n\ntype MediaType = 'min' | 'max';\ntype MediaWidth = string | number;\ntype ResponsiveStyles = Styles;\n\nfunction responsive(type: MediaType, size: MediaWidth, styles: ResponsiveStyles): ResponsiveStyles {\n const unit = typeof size === 'number' ? 'px' : '';\n return {\n [`@media (${type}-width: ${size}${unit})`]: styles,\n };\n}\n\n/**\n * responsiveMin - provides a @media jss properties for min-width\n * @param {MediaWidth} size - the width value, with or without unit of the media\n * @param {ResponsiveStyles} styles - the properties to apply for the media width\n * @returns {ResponsiveStyles} - the media properties\n *\n * @example\n * ```js\n * const useStyles = createUseStyles({\n * width: '100%',\n * ...responsiveMin(480, {\n * width: '100px'\n * })\n * });\n * // result: 100% <= 480px > 100px\n * ```\n */\nexport function responsiveMin(size: MediaWidth, styles: ResponsiveStyles): ResponsiveStyles {\n return responsive('min', size, styles);\n}\n\n/**\n * responsiveMax - provides a @media jss properties for min-width\n * @param {MediaWidth} size - the width value, with or without unit of the media\n * @param {ResponsiveStyles} styles - the properties to apply for the media width\n * @returns {ResponsiveStyles} - the media properties\n *\n * @example\n * ```js\n * const useStyles = createUseStyles({\n * width: '100px',\n * ...responsiveMax(480, {\n * width: '100%'\n * })\n * });\n * // result: 100% <= 480px > 100px\n * ```\n */\nexport function responsiveMax(size: MediaWidth, styles: ResponsiveStyles): ResponsiveStyles {\n return responsive('max', size, styles);\n}\n","import { createSurveyStyles } from '@sm/webassets';\nimport { responsiveMax } from '~app/helpers/responsive';\n\ntype SurveStyleNames =\n | 'surveyHeaderContainer'\n | 'surveyToolbarContainer'\n | 'surveyTitleContainer'\n | 'pageHeaderContainer'\n | 'layoutMainContainer'\n | 'questionContainer'\n | 'pageFooterContainer'\n | 'surveyFooterContainer'\n | 'pageNavigationContainer'\n | 'navButtons';\n\ntype SurveyStyleProps = { offsetTop: number };\nconst useStyles = createSurveyStyles({\n // Document banner container\n surveyHeaderContainer: {},\n // Exit-link container\n surveyToolbarContainer: {},\n // Survey Title container\n surveyTitleContainer: {},\n // Page Header container\n pageHeaderContainer: {},\n // Main container\n layoutMainContainer: {\n marginTop: ({ offsetTop }) => `${offsetTop}px`,\n },\n // Form\n questionContainer: {},\n // Page Footer Container\n pageFooterContainer: {},\n // May not be used\n surveyFooterContainer: {},\n // Page Navigation\n pageNavigationContainer: {\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'stretch',\n },\n\n // Page Navigation Buttons\n navButtons: {\n '&:not(:last-child):not(:only-of-type)': {\n marginRight: '5px',\n },\n // TODO: use breakpoints from theme; remove dependency from util method\n // if only one button, expand to fill <= 480px\n ...responsiveMax(480, {\n '&:only-of-type': {\n width: '100%',\n },\n }),\n },\n});\n\nexport default useStyles;\n","/* eslint-disable no-console */\n/* eslint-disable react/prop-types */\nimport React, { createContext, useMemo, useState, FC } from 'react';\nimport { GetCurrentSurveySessionQuery } from '~lib/generatedGqlTypes';\n\ntype Survey = GetCurrentSurveySessionQuery['surveyTakingSession']['survey'];\ntype ActiveSurveyPage = GetCurrentSurveySessionQuery['surveyTakingSession']['currentPage'];\n\ntype SurveyContextType = {\n survey?: Survey;\n activePage?: ActiveSurveyPage;\n nextPage(): void;\n previousPage(): void;\n};\nexport const SurveyContext = createContext({\n survey: undefined,\n activePage: undefined,\n nextPage: () => {},\n previousPage: () => {},\n});\n\ntype SurveyProviderProps = {\n children: React.ReactNode;\n survey: Survey;\n currentPage: GetCurrentSurveySessionQuery['surveyTakingSession']['currentPage'];\n};\n\nconst SurveyProvider: FC = ({ children, survey: initialSurvey, currentPage }) => {\n // Survey only rendering first page until we have information about who is responsible for providing next page.\n // When we get more information about how we will be acquiring other pages we will add those mechanisms here.\n const activePage = useMemo(() => currentPage, [currentPage]);\n // Survey currently holds error state, setSurvey will need to be called when upstream actions are hooked up.\n const [survey] = useState(initialSurvey);\n const nextPage = (): void => {\n console.log('Use this method to get the next page. Pending decision about pagination');\n };\n const previousPage = (): void => {\n console.log('Use this method to get the previous page. Pending decision about pagination');\n };\n\n return (\n \n <>{children}>\n \n );\n};\n\nexport default SurveyProvider;\n","/* eslint-disable react/prop-types */\nimport { /* AnswerObject, */ OpenEndedAnswerType } from '@sm/question-widgets/respondent-survey';\nimport React, { createContext, useState, useCallback, useContext, FC } from 'react';\n\nimport { GetCurrentSurveySessionQuery } from '~lib/generatedGqlTypes';\nimport { SurveyContext } from './SurveyContext';\n\n// TODO: Add to `@sm/question-widgets/respondent-survey` types\ntype AnswerObject = {\n [key: string]: OpenEndedAnswerType;\n};\n\ntype CurrentPageContextType = {\n questions?: NonNullable<\n NonNullable['questionsQM']\n >['items'];\n answers?: AnswerObject | {};\n updateAnswers(answer: OpenEndedAnswerType): void;\n};\n\nexport const CurrentPageContext = createContext({\n questions: undefined,\n answers: undefined,\n updateAnswers: answer => {},\n});\n\ntype CurrentPageProviderProps = {\n children: React.ReactNode;\n};\n\nexport const CurrentPageProvider: FC = ({ children }) => {\n // Local storage -> overrides data from survey.\n // Local storage should be cleared when we hit the next page button.\n\n const { activePage } = useContext(SurveyContext);\n const [questions] = useState(activePage?.questionsQM?.items);\n\n const initialAnswers = questions?.reduce(\n (acc, curr) => ({ ...acc, [curr.id]: { questionId: curr.id, value: '', touched: false, isDirty: false } }),\n {}\n );\n\n // Hydrate answers\n const [answers, setAnswers] = useState(initialAnswers);\n\n const updateAnswers = useCallback(\n answer => {\n setAnswers({\n ...answers,\n [answer.questionID]: answer,\n });\n },\n [answers]\n );\n\n return (\n \n <>{children}>\n \n );\n};\n\nexport default CurrentPageProvider;\n","import { createSurveyStyles } from '@sm/webassets';\n\ntype StyleClassNames = 'logoContainer' | 'logoWrapper' | 'logoImage';\ntype StyleProps = {\n size: string | null;\n height?: number;\n width?: number;\n verticalAlignment: string;\n};\n\n// Logo alignment comes in as `verticalAlignment`, and the keys represent\n// values available from GraphQL API (SurveyLogoAlignment). Presently, the\n// only options to choose from are: CENTER, LEFT_TOP, RIGHT_TOP.\nconst surveyLogoAlignment: Record = {\n CENTER: { justify: 'center', align: 'center' },\n LEFT_TOP: { justify: 'flex-start', align: 'flex-start' },\n RIGHT_TOP: { justify: 'flex-end', align: 'flex-start' },\n\n // Here for other values when implemented\n BASELINE: { justify: 'flex-start', align: 'baseline' },\n BOTTOM: { justify: 'center', align: 'flex-end' },\n LEFT_MIDDLE: { justify: 'flex-start', align: 'center' },\n RIGHT_MIDDLE: { justify: 'flex-end', align: 'center' },\n TOP: { justify: 'center', align: 'flex-start' },\n};\n\nconst isActualSize = (size: string | null): boolean => {\n // presently, `null` is returned in place of 'ACTUAL', here for any regression.\n return size === null || size === 'ACTUAL';\n};\n\nconst useStyles = createSurveyStyles({\n logoContainer: {\n display: 'flex',\n width: '100%',\n justifyContent: ({ verticalAlignment }) => surveyLogoAlignment[verticalAlignment].justify,\n alignItems: ({ verticalAlignment }) => surveyLogoAlignment[verticalAlignment].align,\n },\n logoWrapper: {\n width: ({ size, width }) => (!isActualSize(size) ? width : 'auto'),\n height: ({ size, height }) => (!isActualSize(size) ? height : 'auto'),\n },\n logoImage: {\n width: '100%',\n height: '100%',\n },\n});\n\nexport default useStyles;\n","import React from 'react';\nimport useStyles from './useStyles';\n\nexport type Props = {\n id?: Element['id'];\n enabled?: boolean;\n size?: string | null; // used to decide if width/height is applied\n height?: number;\n width?: number;\n verticalAlignment?: string;\n image: {\n url: string;\n };\n altText?: string;\n children?: never;\n} & React.HTMLAttributes;\n\nexport function Logo({\n id,\n enabled = true,\n size = null,\n height,\n width,\n image: { url },\n verticalAlignment = 'LEFT_TOP',\n altText = '',\n ...props\n}: Props): React.ReactElement {\n const { logoContainer, logoWrapper, logoImage } = useStyles({ verticalAlignment, size, width, height });\n return (\n \n
\n
\n \n
\n
\n \n );\n}\n\nexport default Logo;\n","import React, { useState, useCallback, useEffect } from 'react';\nimport useLayoutEffect from '~app/hooks/useIsomorphicLayoutEffect';\n\nconst useCenterContainerOffset = (\n centerTarget: React.RefObject,\n viewportExclusions: number[],\n heightExclusions: number[],\n adjustment?: (margin: number) => number\n): number => {\n const [offsetTop, setOffsetTop] = useState(0);\n const calculateOffsetTop = useCallback(() => {\n const winH = window.innerHeight;\n const targetContainerHeight = (centerTarget?.current as HTMLElement)?.offsetHeight;\n const viewport = winH - viewportExclusions.reduce((a, i) => a + i, 0);\n const excludeHeight = heightExclusions.reduce((a, i) => a + i, 0);\n let margin = targetContainerHeight > 0 ? Math.max(10, viewport / 2 - targetContainerHeight / 2 - excludeHeight) : 0;\n\n margin = adjustment?.(margin) || margin;\n setOffsetTop(margin);\n }, [centerTarget, viewportExclusions, heightExclusions, adjustment]);\n\n useLayoutEffect(() => {\n window.addEventListener('resize', calculateOffsetTop);\n return () => window.removeEventListener('resize', calculateOffsetTop);\n }, [calculateOffsetTop]);\n\n useEffect(() => {\n calculateOffsetTop();\n }, [centerTarget, calculateOffsetTop]);\n\n return offsetTop;\n};\n\nexport default useCenterContainerOffset;\n","import React, { useContext } from 'react';\nimport { T } from '@sm/intl';\nimport { OpenEndedAnswerType, RespondentCapabilityProvider } from '@sm/question-widgets/respondent-survey';\nimport { ExternalQuestion } from '@sm/question-definitions';\nimport { BypassBlock } from '~app/components/core/BypassBlock';\nimport { OQAATProvider, ScrollAnchor } from '~app/components/Survey/OneQuestionAtATime';\nimport { ExitLink } from '~app/components/Survey/ExitLink';\nimport { SurveyTitle } from '~app/components/Survey/SurveyTitleV2';\nimport createHeadingLevel from './helper/createHeadingLevel';\nimport SurveyFooter from '~app/components/Survey/FooterContainer';\nimport { PageTitle } from '~app/components/Survey/PageTitle';\nimport { PageDescription } from '~app/components/Survey/PageDescription';\nimport { SurveyButton } from '~app/components/Survey/SurveyButton';\nimport { Layout } from './components/Layout';\nimport { QuestionList } from './components/QuestionList';\nimport { PersistentProgressBar } from '~app/components/Survey/PersistentProgressBar';\nimport extrapolateSurveyData from './helper/extrapolateSurveyData';\nimport useStyles from './useStyles';\nimport { SurveyContext } from './SurveyContext';\nimport { CurrentPageContext } from './CurrentPageContext';\nimport { Logo } from './components/Logo';\nimport useCenterContainerOffset from './hooks/useCenterContainerOffset';\n\nconst COPY = {\n SKIP_LINK_CONTENT: {\n id: 'SurveyTaking.SkipLinkContent',\n defaultMessage: 'Skip to content',\n description: '[Type: label][Vis: med] - Skip to content link label (Accessibiility)',\n },\n};\n\nconst Survey: React.FC = () => {\n const { survey, activePage } = useContext(SurveyContext);\n const { questions = [], answers = {}, updateAnswers } = useContext(CurrentPageContext);\n\n const {\n isSurveyTitleEnabled,\n showPageTitles,\n langCode,\n isFooterEnabled,\n isLogoEnabled,\n isAutoScroll,\n totalQuestionsCount,\n isQuestionNumberingEnabled,\n showRequiredQuestionAsterisks,\n logoProps,\n surveyTitleAlignment,\n themeVersion,\n } = extrapolateSurveyData(survey);\n\n // TODO: Refactor isRTL to use provider\n const isRTL = ['ar', 'he', 'fa'].includes(langCode || '');\n const surveyTitleHTML = survey?.titleHTML;\n\n const surveyId = survey?.id;\n /**\n * Page Header\n * calculate proper Page Title and Description based on current page\n */\n const hasPageDescription = !!activePage?.subHeading;\n const pageDescription = activePage?.subHeading;\n const pageTitle = activePage?.heading;\n /**\n * Skip to content will attempt to move the focus position (without actually focusing)\n * to the Survey Title (if exists), then Page title (if exists), finially to the\n * main-content container ().\n * `tabIndex={-1}` is added to each element (backwards compat with <= IE 11)\n */\n const isExitLinkEnabled = true;\n const isProgressBarEnabled = true;\n const surveyTitleId = `survey-title-${surveyId}`;\n const pageTitleId = `page-title-${surveyId}`;\n const mainContentId = `main-content-${surveyId}`;\n\n let skipContentId = mainContentId;\n if (showPageTitles) {\n skipContentId = pageTitleId;\n }\n const nextHeadingElement = createHeadingLevel();\n\n const layoutContainerRef = React.useRef(null);\n const pageTitleRef = React.useRef(null);\n const persistentProgressRef = React.useRef(null);\n const partialContainerRef = React.useRef(null);\n const logoContainerRef = React.useRef(null);\n const toolbarContainerRef = React.useRef(null);\n const topContainerRef = React.useRef(null);\n const mainContainerRef = React.useRef(null);\n const mainRef = React.useRef(null);\n\n const offsetTop = useCenterContainerOffset(\n pageTitleRef,\n [persistentProgressRef?.current?.offsetHeight || 0],\n [topContainerRef?.current?.offsetHeight || 0],\n (margin: number) => {\n if (themeVersion === 'V3') {\n const cr = window.getComputedStyle(mainContainerRef?.current as Element);\n return Math.abs(margin / 2 - parseInt(cr.paddingTop, 10));\n }\n return margin;\n }\n );\n\n const {\n surveyTitleContainer,\n pageHeaderContainer,\n layoutMainContainer,\n questionContainer,\n pageFooterContainer,\n pageNavigationContainer,\n navButtons,\n } = useStyles({ offsetTop });\n\n const layoutContainerOptions = {\n layoutContainer: { ref: layoutContainerRef },\n partialContainer: { ref: partialContainerRef },\n topContainer: { ref: topContainerRef },\n mainContainer: { className: layoutMainContainer, ref: mainContainerRef },\n logoContainer: { ref: logoContainerRef },\n toolbarContainer: { ref: toolbarContainerRef },\n };\n\n const settings = {\n survey: {\n numberingEnabled: isQuestionNumberingEnabled,\n asteriskEnabled: showRequiredQuestionAsterisks,\n },\n };\n\n const upstreamActions = {\n saveQuestion: (externalQuestion: ExternalQuestion, answer: OpenEndedAnswerType) => {\n if (questions) {\n // updateQuestions(externalQuestion);\n updateAnswers(answer);\n }\n // nextQuestion();\n },\n onUpdate: (answer: OpenEndedAnswerType) => {\n // If we are using this approach should remove state from AnswerSingleTextbox.visualization\n updateAnswers(answer);\n },\n };\n\n return (\n \n \n \n \n \n \n }\n tools={isExitLinkEnabled && Exit}\n options={layoutContainerOptions}\n >\n \n