From: Wouter Admiraal Date: Fri, 26 May 2023 11:12:24 +0000 (+0200) Subject: SONAR-19391 Prepare new layout structure X-Git-Tag: 10.1.0.73491~152 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=d419d9392f7274dacfefc619bd0cee2fb71c4e7c;p=sonarqube.git SONAR-19391 Prepare new layout structure --- diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumb.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumb.tsx deleted file mode 100644 index d1d3c14888a..00000000000 --- a/server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumb.tsx +++ /dev/null @@ -1,62 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import * as React from 'react'; -import Tooltip from '../../../components/controls/Tooltip'; -import { collapsePath, limitComponentName } from '../../../helpers/path'; -import { ComponentMeasure, ComponentMeasureIntern } from '../../../types/types'; - -interface Props { - canBrowse: boolean; - component: ComponentMeasure; - isLast: boolean; - handleSelect: (component: ComponentMeasureIntern) => void; -} - -export default class Breadcrumb extends React.PureComponent { - handleClick = (event: React.MouseEvent) => { - event.preventDefault(); - event.currentTarget.blur(); - this.props.handleSelect(this.props.component); - }; - - render() { - const { canBrowse, component, isLast } = this.props; - const isPath = component.qualifier === 'DIR'; - const componentName = isPath - ? collapsePath(component.name, 15) - : limitComponentName(component.name); - const breadcrumbItem = canBrowse ? ( - - {componentName} - - ) : ( - {componentName} - ); - - return ( - - - {breadcrumbItem} - - {!isLast && } - - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumbs.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumbs.tsx deleted file mode 100644 index d86ac9cae16..00000000000 --- a/server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumbs.tsx +++ /dev/null @@ -1,115 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import * as React from 'react'; -import { getBreadcrumbs } from '../../../api/components'; -import { getBranchLikeQuery, isSameBranchLike } from '../../../helpers/branch-like'; -import { KeyboardKeys } from '../../../helpers/keycodes'; -import { BranchLike } from '../../../types/branch-like'; -import { ComponentMeasure, ComponentMeasureIntern } from '../../../types/types'; -import Breadcrumb from './Breadcrumb'; - -interface Props { - backToFirst: boolean; - branchLike?: BranchLike; - className?: string; - component: ComponentMeasure; - handleSelect: (component: ComponentMeasureIntern) => void; - rootComponent: ComponentMeasure; -} - -interface State { - breadcrumbs: ComponentMeasure[]; -} - -export default class Breadcrumbs extends React.PureComponent { - mounted = false; - state: State = { breadcrumbs: [] }; - - componentDidMount() { - this.mounted = true; - this.fetchBreadcrumbs(); - document.addEventListener('keydown', this.handleKeyDown); - } - - componentDidUpdate(prevProps: Props) { - if ( - this.props.component !== prevProps.component || - !isSameBranchLike(prevProps.branchLike, this.props.branchLike) - ) { - this.fetchBreadcrumbs(); - } - } - - componentWillUnmount() { - this.mounted = false; - document.removeEventListener('keydown', this.handleKeyDown); - } - - handleKeyDown = (event: KeyboardEvent) => { - if (event.key === KeyboardKeys.LeftArrow) { - event.preventDefault(); - const { breadcrumbs } = this.state; - if (breadcrumbs.length > 1) { - const idx = this.props.backToFirst ? 0 : breadcrumbs.length - 2; - this.props.handleSelect(breadcrumbs[idx]); - } - } - }; - - fetchBreadcrumbs = () => { - const { branchLike, component, rootComponent } = this.props; - const isRoot = component.key === rootComponent.key; - if (isRoot) { - if (this.mounted) { - this.setState({ breadcrumbs: [component] }); - } - return; - } - getBreadcrumbs({ component: component.key, ...getBranchLikeQuery(branchLike) }).then( - (breadcrumbs) => { - if (this.mounted) { - this.setState({ breadcrumbs }); - } - }, - () => {} - ); - }; - - render() { - const { breadcrumbs } = this.state; - if (breadcrumbs.length <= 0) { - return null; - } - const lastItem = breadcrumbs[breadcrumbs.length - 1]; - return ( -
- {breadcrumbs.map((component) => ( - - ))} -
- ); - } -} diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx index a99b3684ad6..2aba7aba066 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx @@ -17,7 +17,16 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { withTheme } from '@emotion/react'; import styled from '@emotion/styled'; +import { + DeferredSpinner, + LargeCenteredLayout, + Note, + PageContentFontWrapper, + themeBorder, + themeColor, +} from 'design-system'; import { debounce, keyBy } from 'lodash'; import * as React from 'react'; import { Helmet } from 'react-helmet-async'; @@ -25,23 +34,14 @@ import { getMeasuresWithPeriod } from '../../../api/measures'; import { getAllMetrics } from '../../../api/metrics'; import withBranchStatusActions from '../../../app/components/branch-status/withBranchStatusActions'; import { ComponentContext } from '../../../app/components/componentContext/ComponentContext'; -import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper'; -import HelpTooltip from '../../../components/controls/HelpTooltip'; import Suggestions from '../../../components/embed-docs-modal/Suggestions'; import { Location, Router, withRouter } from '../../../components/hoc/withRouter'; import { enhanceMeasure } from '../../../components/measure/utils'; import '../../../components/search-navigator.css'; -import { Alert } from '../../../components/ui/Alert'; import { getBranchLikeQuery, isPullRequest, isSameBranchLike } from '../../../helpers/branch-like'; import { translate } from '../../../helpers/l10n'; -import { - addSideBarClass, - addWhitePageClass, - removeSideBarClass, - removeWhitePageClass, -} from '../../../helpers/pages'; import { BranchLike } from '../../../types/branch-like'; -import { ComponentQualifier, isPortfolioLike } from '../../../types/component'; +import { ComponentQualifier } from '../../../types/component'; import { ComponentMeasure, Dict, @@ -53,6 +53,7 @@ import { import Sidebar from '../sidebar/Sidebar'; import '../style.css'; import { + Query, banQualityGateMeasure, getMeasuresPageMetricKeys, groupByDomains, @@ -61,7 +62,6 @@ import { hasTree, hasTreemap, parseQuery, - Query, serializeQuery, sortMeasures, } from '../utils'; @@ -111,7 +111,7 @@ class ComponentMeasuresApp extends React.PureComponent { ); } - componentDidUpdate(prevProps: Props, prevState: State) { + componentDidUpdate(prevProps: Props) { const prevQuery = parseQuery(prevProps.location.query); const query = parseQuery(this.props.location.query); @@ -122,17 +122,10 @@ class ComponentMeasuresApp extends React.PureComponent { ) { this.fetchMeasures(this.state.metrics); } - - if (prevState.measures.length === 0 && this.state.measures.length > 0) { - addWhitePageClass(); - addSideBarClass(); - } } componentWillUnmount() { this.mounted = false; - removeWhitePageClass(); - removeSideBarClass(); } fetchMeasures(metrics: State['metrics']) { @@ -221,25 +214,31 @@ class ComponentMeasuresApp extends React.PureComponent { renderContent = (displayOverview: boolean, query: Query, metric?: Metric) => { const { branchLike, component } = this.props; const { leakPeriod } = this.state; + if (displayOverview) { return ( - + + + ); } if (!metric) { - return ; + return ( + + + + ); } const hideDrilldown = @@ -248,40 +247,32 @@ class ComponentMeasuresApp extends React.PureComponent { if (hideDrilldown) { return ( -
-
-
{translate('component_measures.details_are_not_available')}
-
-
+ + {translate('component_measures.details_are_not_available')} + ); } return ( - + + + ); }; render() { - if (this.state.loading) { - return ( -
- -
- ); - } - const { branchLike } = this.props; const { measures } = this.state; const { canBrowseAllChildProjects, qualifier } = this.props.component; @@ -291,58 +282,40 @@ class ComponentMeasuresApp extends React.PureComponent { const metric = this.getSelectedMetric(query, displayOverview); return ( -
+ - {measures.length > 0 ? ( -
- - {({ top }) => ( -
-
- {!canBrowseAllChildProjects && isPortfolioLike(qualifier) && ( - - - {translate('component_measures.not_all_measures_are_shown')} - - - - )} -
- -
-
-
- )} -
- {this.renderContent(displayOverview, query, metric)} -
- ) : ( - - )} -
+ + + + {measures.length > 0 ? ( +
+ +
+ {this.renderContent(displayOverview, query, metric)} +
+
+ ) : ( + + + + )} +
+ ); } } -const AlertContent = styled.div` - display: flex; - align-items: center; -`; - /* * This needs to be refactored: the issue * is that we can't use the usual withComponentContext HOC, because the type @@ -357,3 +330,9 @@ function AppWithComponentContext() { } export default AppWithComponentContext; + +const StyledMain = withTheme(styled.main` + background-color: ${themeColor('filterbar')}; + background-color: ${themeColor('pageBlock')}; + border: ${themeBorder('default', 'pageBlockBorder')}l; +`); diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx index 304be402e5e..5f241e9e2c4 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx @@ -20,9 +20,9 @@ import * as React from 'react'; import { getComponentTree } from '../../../api/components'; import { getMeasures } from '../../../api/measures'; +import SourceViewer from '../../../components/SourceViewer/SourceViewer'; import A11ySkipTarget from '../../../components/a11y/A11ySkipTarget'; import { Router } from '../../../components/hoc/withRouter'; -import SourceViewer from '../../../components/SourceViewer/SourceViewer'; import PageActions from '../../../components/ui/PageActions'; import { getBranchLikeQuery, isSameBranchLike } from '../../../helpers/branch-like'; import { getComponentMeasureUniqueKey } from '../../../helpers/component'; @@ -48,11 +48,11 @@ import { import { complementary } from '../config/complementary'; import FilesView from '../drilldown/FilesView'; import TreeMapView from '../drilldown/TreeMapView'; -import { enhanceComponent, Query } from '../utils'; -import Breadcrumbs from './Breadcrumbs'; +import { Query, enhanceComponent } from '../utils'; import MeasureContentHeader from './MeasureContentHeader'; import MeasureHeader from './MeasureHeader'; import MeasureViewSelect from './MeasureViewSelect'; +import MeasuresBreadcrumbs from './MeasuresBreadcrumbs'; interface Props { branchLike?: BranchLike; @@ -352,81 +352,68 @@ export default class MeasureContent extends React.PureComponent { const selectedIdx = this.getSelectedIndex(); return ( -
(this.container = container)} - > +
(this.container = container)}> -
-
-
- - } - right={ -
- {!isFileComponent && metric && ( - <> -
- {translate('component_measures.view_as')} -
- - - - - )} + + } + right={ +
+ {!isFileComponent && metric && ( + <> +
+ {translate('component_measures.view_as')}
- } - /> + + + + + )}
+ } + /> + + + {isFileComponent ? ( +
+
-
- -
- - {isFileComponent ? ( -
- -
- ) : ( - this.renderMeasure() - )} -
+ ) : ( + this.renderMeasure() + )}
); } diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentHeader.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentHeader.tsx index 55e74cd8421..8f061c91919 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentHeader.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentHeader.tsx @@ -26,9 +26,9 @@ interface Props { export default function MeasureContentHeader({ left, right }: Props) { return ( -
-
{left}
-
{right}
+
+
{left}
+
{right}
); } diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx index 501ec9fac2e..2bac35e3559 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx @@ -19,8 +19,8 @@ */ import * as React from 'react'; import { getComponentLeaves } from '../../../api/components'; -import A11ySkipTarget from '../../../components/a11y/A11ySkipTarget'; import SourceViewer from '../../../components/SourceViewer/SourceViewer'; +import A11ySkipTarget from '../../../components/a11y/A11ySkipTarget'; import DeferredSpinner from '../../../components/ui/DeferredSpinner'; import PageActions from '../../../components/ui/PageActions'; import { getBranchLikeQuery, isSameBranchLike } from '../../../helpers/branch-like'; @@ -38,9 +38,9 @@ import { } from '../../../types/types'; import BubbleChart from '../drilldown/BubbleChart'; import { BUBBLES_FETCH_LIMIT, enhanceComponent, getBubbleMetrics, hasFullMeasures } from '../utils'; -import Breadcrumbs from './Breadcrumbs'; import LeakPeriodLegend from './LeakPeriodLegend'; import MeasureContentHeader from './MeasureContentHeader'; +import MeasuresBreadcrumbs from './MeasuresBreadcrumbs'; interface Props { branchLike?: BranchLike; @@ -154,43 +154,34 @@ export default class MeasureOverview extends React.PureComponent { const { branchLike, className, component, leakPeriod, loading, rootComponent } = this.props; const displayLeak = hasFullMeasures(branchLike); return ( -
-
- +
+ -
-
- - } - right={ - - } - /> -
-
-
-
-
- {leakPeriod && displayLeak && ( - - )} -
- - {!loading && this.renderContent()} -
-
+ + } + right={ + + } + /> + {leakPeriod && displayLeak && ( + + )} + + + + {!loading && this.renderContent()} +
); } } diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasuresBreadcrumbs.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasuresBreadcrumbs.tsx new file mode 100644 index 00000000000..b5c578c07e8 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasuresBreadcrumbs.tsx @@ -0,0 +1,129 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { Breadcrumbs, HoverLink } from 'design-system'; +import * as React from 'react'; +import { getBreadcrumbs } from '../../../api/components'; +import { getBranchLikeQuery, isSameBranchLike } from '../../../helpers/branch-like'; +import { KeyboardKeys } from '../../../helpers/keycodes'; +import { translate } from '../../../helpers/l10n'; +import { collapsePath, limitComponentName } from '../../../helpers/path'; +import { BranchLike } from '../../../types/branch-like'; +import { ComponentQualifier } from '../../../types/component'; +import { ComponentMeasure, ComponentMeasureIntern } from '../../../types/types'; + +interface Props { + backToFirst: boolean; + branchLike?: BranchLike; + className?: string; + component: ComponentMeasure; + handleSelect: (component: ComponentMeasureIntern) => void; + rootComponent: ComponentMeasure; +} + +interface State { + breadcrumbs: ComponentMeasure[]; +} + +export default class MeasuresBreadcrumbs extends React.PureComponent { + mounted = false; + state: State = { breadcrumbs: [] }; + + componentDidMount() { + this.mounted = true; + this.fetchBreadcrumbs(); + document.addEventListener('keydown', this.handleKeyDown); + } + + componentDidUpdate(prevProps: Props) { + if ( + this.props.component !== prevProps.component || + !isSameBranchLike(prevProps.branchLike, this.props.branchLike) + ) { + this.fetchBreadcrumbs(); + } + } + + componentWillUnmount() { + this.mounted = false; + document.removeEventListener('keydown', this.handleKeyDown); + } + + handleKeyDown = (event: KeyboardEvent) => { + if (event.key === KeyboardKeys.LeftArrow) { + event.preventDefault(); + const { breadcrumbs } = this.state; + if (breadcrumbs.length > 1) { + const idx = this.props.backToFirst ? 0 : breadcrumbs.length - 2; + this.props.handleSelect(breadcrumbs[idx]); + } + } + }; + + fetchBreadcrumbs = () => { + const { branchLike, component, rootComponent } = this.props; + const isRoot = component.key === rootComponent.key; + if (isRoot) { + if (this.mounted) { + this.setState({ breadcrumbs: [component] }); + } + return; + } + getBreadcrumbs({ component: component.key, ...getBranchLikeQuery(branchLike) }).then( + (breadcrumbs) => { + if (this.mounted) { + this.setState({ breadcrumbs }); + } + }, + () => {} + ); + }; + + render() { + const { breadcrumbs } = this.state; + + if (breadcrumbs.length <= 0) { + return null; + } + + return ( + + {breadcrumbs.map((component) => ( + ) => { + event.preventDefault(); + event.currentTarget.blur(); + this.props.handleSelect(component); + }} + > + {component.qualifier === ComponentQualifier.Directory + ? collapsePath(component.name, 15) + : limitComponentName(component.name)} + + ))} + + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasuresEmpty.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasuresEmpty.tsx index 4bfc270d36d..72bcaca5599 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasuresEmpty.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasuresEmpty.tsx @@ -17,13 +17,10 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { Note } from 'design-system/lib'; import * as React from 'react'; import { translate } from '../../../helpers/l10n'; export default function MeasuresEmpty() { - return ( -
-
{translate('component_measures.empty')}
-
- ); + return {translate('component_measures.empty')}; } diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx b/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx index 7ffcfb03c8d..0b786954998 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx @@ -17,72 +17,117 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { withTheme } from '@emotion/react'; +import styled from '@emotion/styled'; +import { + FlagMessage, + LAYOUT_FOOTER_HEIGHT, + LAYOUT_GLOBAL_NAV_HEIGHT, + LAYOUT_PROJECT_NAV_HEIGHT, + themeBorder, + themeColor, +} from 'design-system/lib'; import * as React from 'react'; import A11ySkipTarget from '../../../components/a11y/A11ySkipTarget'; +import HelpTooltip from '../../../components/controls/HelpTooltip'; import { translate } from '../../../helpers/l10n'; +import useFollowScroll from '../../../hooks/useFollowScroll'; +import { isPortfolioLike } from '../../../types/component'; import { Dict, MeasureEnhanced } from '../../../types/types'; -import { groupByDomains, KNOWN_DOMAINS, PROJECT_OVERVEW, Query } from '../utils'; +import { KNOWN_DOMAINS, PROJECT_OVERVEW, Query, groupByDomains } from '../utils'; import DomainFacet from './DomainFacet'; import ProjectOverviewFacet from './ProjectOverviewFacet'; interface Props { + canBrowseAllChildProjects: boolean; measures: MeasureEnhanced[]; + qualifier: string; selectedMetric: string; showFullMeasures: boolean; updateQuery: (query: Partial) => void; } -interface State { - openFacets: Dict; -} - -export default class Sidebar extends React.PureComponent { - static getDerivedStateFromProps(props: Props, state: State) { - return { openFacets: getOpenFacets(state.openFacets, props) }; - } +export default function Sidebar(props: Props) { + const { + showFullMeasures, + canBrowseAllChildProjects, + qualifier, + updateQuery, + selectedMetric, + measures, + } = props; + const [openFacets, setOpenFacets] = React.useState(getOpenFacets({}, props)); + const { top: topScroll } = useFollowScroll(); - state: State = { - openFacets: {}, - }; + const handleToggleFacet = React.useCallback( + (name: string) => { + setOpenFacets((openFacets) => ({ ...openFacets, [name]: !openFacets[name] })); + }, + [setOpenFacets] + ); - toggleFacet = (name: string) => { - this.setState(({ openFacets }) => ({ - openFacets: { ...openFacets, [name]: !openFacets[name] }, - })); - }; + const handleChangeMetric = React.useCallback( + (metric: string) => { + updateQuery({ metric }); + }, + [updateQuery] + ); - changeMetric = (metric: string) => { - this.props.updateQuery({ metric }); - }; + const distanceFromBottom = topScroll + window.innerHeight - document.body.clientHeight; + const footerVisibleHeight = + distanceFromBottom > -LAYOUT_FOOTER_HEIGHT ? LAYOUT_FOOTER_HEIGHT + distanceFromBottom : 0; - render() { - const { showFullMeasures } = this.props; - return ( -