Skip to content

Commit

Permalink
feat: APPS-2946 Create Two Column Layout component (#614)
Browse files Browse the repository at this point in the history
* feat: begin sidebar structure, start drawio docs

* feat: more progress side

* feat: progress on 2col, stories

* chore: finish stories, tests

* chore: cleanup, documentation

* fix: double padding

* fix: inconsistent spacing when card-meta is above sidebar

---------

Co-authored-by: Jess Divers <[email protected]>
  • Loading branch information
farosFreed and Jess Divers authored Sep 24, 2024
1 parent e9f40c4 commit cc5d52d
Show file tree
Hide file tree
Showing 6 changed files with 511 additions and 0 deletions.
131 changes: 131 additions & 0 deletions src/documentation/TwoColLayoutWSticky.drawio
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<mxfile host="65bd71144e">
<diagram id="k3MkfsP0TAj-Hgy-79KS" name="Page-1">
<mxGraphModel dx="650" dy="1500" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="2" value="" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" vertex="1" parent="1">
<mxGeometry x="20" y="10" width="380" height="380" as="geometry"/>
</mxCell>
<mxCell id="3" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#ae4132;dashed=1;strokeWidth=3;" vertex="1" parent="1">
<mxGeometry x="30" y="20" width="220" height="360" as="geometry"/>
</mxCell>
<mxCell id="9" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" vertex="1" parent="1">
<mxGeometry x="440" y="10" width="160" height="610" as="geometry"/>
</mxCell>
<mxCell id="10" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fad9d5;strokeColor=#ae4132;" vertex="1" parent="1">
<mxGeometry x="450" y="20" width="140" height="90" as="geometry"/>
</mxCell>
<mxCell id="11" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#b0e3e6;strokeColor=#0e8088;" vertex="1" parent="1">
<mxGeometry x="450" y="120" width="140" height="90" as="geometry"/>
</mxCell>
<mxCell id="12" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fad9d5;strokeColor=#ae4132;" vertex="1" parent="1">
<mxGeometry x="450" y="220" width="140" height="120" as="geometry"/>
</mxCell>
<mxCell id="14" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeWidth=3;dashed=1;strokeColor=#004C99;" vertex="1" parent="1">
<mxGeometry x="260" y="20" width="130" height="360" as="geometry"/>
</mxCell>
<mxCell id="18" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeColor=#000033;startArrow=none;" edge="1" parent="1" source="20">
<mxGeometry relative="1" as="geometry">
<mxPoint x="210" y="190" as="sourcePoint"/>
<mxPoint x="325" y="370" as="targetPoint"/>
<Array as="points">
<mxPoint x="325" y="270"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="21" value="" style="endArrow=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;strokeColor=#000033;" edge="1" parent="1" source="4" target="20">
<mxGeometry relative="1" as="geometry">
<mxPoint x="325" y="150" as="sourcePoint"/>
<mxPoint x="325" y="380" as="targetPoint"/>
<Array as="points"/>
</mxGeometry>
</mxCell>
<mxCell id="20" value="Sticky" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=#FFF9CF;strokeWidth=4;fontColor=#050505;" vertex="1" parent="1">
<mxGeometry x="295" y="220" width="60" height="30" as="geometry"/>
</mxCell>
<mxCell id="24" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="450" y="350" width="140" height="90" as="geometry"/>
</mxCell>
<mxCell id="28" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="30" y="20" width="220" height="70" as="geometry"/>
</mxCell>
<mxCell id="29" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="30" y="140" width="220" height="120" as="geometry"/>
</mxCell>
<mxCell id="31" value="" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;labelBackgroundColor=none;strokeColor=#FF8000;strokeWidth=2;fontColor=#050505;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="40" y="100" width="200" height="30" as="geometry"/>
</mxCell>
<mxCell id="32" value="slot: primaryTop&amp;nbsp;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=none;strokeWidth=2;fontColor=#050505;" vertex="1" parent="1">
<mxGeometry x="50" y="40" width="190" height="30" as="geometry"/>
</mxCell>
<mxCell id="36" value="" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;labelBackgroundColor=none;strokeColor=#FF8000;strokeWidth=2;fontColor=#050505;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="450" y="120" width="140" height="90" as="geometry"/>
</mxCell>
<mxCell id="39" value="" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;labelBackgroundColor=none;strokeColor=#FF8000;strokeWidth=2;fontColor=#050505;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="450" y="350" width="140" height="90" as="geometry"/>
</mxCell>
<mxCell id="47" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeWidth=3;dashed=1;strokeColor=#00CCCC;" vertex="1" parent="1">
<mxGeometry x="260" y="19" width="130" height="185" as="geometry"/>
</mxCell>
<mxCell id="44" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#ae4132;dashed=1;strokeWidth=3;movable=0;resizable=0;rotatable=0;deletable=0;editable=0;connectable=0;" vertex="1" parent="1">
<mxGeometry x="450" y="20" width="140" height="590" as="geometry"/>
</mxCell>
<mxCell id="4" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#b0e3e6;strokeColor=#0e8088;" vertex="1" parent="1">
<mxGeometry x="260" y="20" width="130" height="70" as="geometry"/>
</mxCell>
<mxCell id="23" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="260" y="100" width="130" height="100" as="geometry"/>
</mxCell>
<mxCell id="49" value="SMALL SCREENS" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=#FFFFFF;strokeWidth=2;fontSize=13;fontColor=#000033;" vertex="1" parent="1">
<mxGeometry x="430" y="-40" width="120" height="30" as="geometry"/>
</mxCell>
<mxCell id="66" value="slot: sidebarTop (mobile only)&amp;nbsp;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=none;strokeWidth=2;fontColor=#050505;" vertex="1" parent="1">
<mxGeometry x="50" y="100" width="190" height="30" as="geometry"/>
</mxCell>
<mxCell id="68" value="slot: primaryMid&amp;nbsp;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=none;strokeWidth=2;fontColor=#050505;" vertex="1" parent="1">
<mxGeometry x="50" y="185" width="190" height="30" as="geometry"/>
</mxCell>
<mxCell id="69" value="" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;labelBackgroundColor=none;strokeColor=#FF8000;strokeWidth=2;fontColor=#050505;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="40" y="273" width="200" height="30" as="geometry"/>
</mxCell>
<mxCell id="70" value="slot: sidebarBottom (mobile only)&amp;nbsp;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=none;strokeWidth=2;fontColor=#050505;" vertex="1" parent="1">
<mxGeometry x="50" y="273" width="190" height="30" as="geometry"/>
</mxCell>
<mxCell id="71" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="30" y="310" width="220" height="70" as="geometry"/>
</mxCell>
<mxCell id="72" value="slot: primaryBottom&amp;nbsp;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=none;strokeWidth=2;fontColor=#050505;" vertex="1" parent="1">
<mxGeometry x="50" y="330" width="190" height="30" as="geometry"/>
</mxCell>
<mxCell id="73" value="slot: sidebarTop (desktop only)&amp;nbsp;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=none;strokeWidth=2;fontColor=#050505;" vertex="1" parent="1">
<mxGeometry x="260" y="40" width="120" height="30" as="geometry"/>
</mxCell>
<mxCell id="74" value="slot: sidebarBottom (desktop only)&amp;nbsp;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=none;strokeWidth=2;fontColor=#050505;" vertex="1" parent="1">
<mxGeometry x="265" y="140" width="120" height="30" as="geometry"/>
</mxCell>
<mxCell id="75" value="XLARGE / LARGE / MEDIUM SCREENS" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=#FFFFFF;strokeWidth=2;fontSize=13;fontColor=#000033;" vertex="1" parent="1">
<mxGeometry x="10" y="-40" width="270" height="30" as="geometry"/>
</mxCell>
<mxCell id="76" value="slot: primaryTop&amp;nbsp;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=none;strokeWidth=2;fontColor=#050505;" vertex="1" parent="1">
<mxGeometry x="450" y="50" width="140" height="30" as="geometry"/>
</mxCell>
<mxCell id="77" value="slot: primaryMid&amp;nbsp;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=none;strokeWidth=2;fontColor=#050505;" vertex="1" parent="1">
<mxGeometry x="450" y="260" width="140" height="30" as="geometry"/>
</mxCell>
<mxCell id="78" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fad9d5;strokeColor=#ae4132;" vertex="1" parent="1">
<mxGeometry x="450" y="450" width="140" height="120" as="geometry"/>
</mxCell>
<mxCell id="79" value="slot: primaryBottom" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=none;strokeWidth=2;fontColor=#050505;" vertex="1" parent="1">
<mxGeometry x="450" y="490" width="140" height="30" as="geometry"/>
</mxCell>
<mxCell id="80" value="slot: sidebarTop&amp;nbsp;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=none;strokeWidth=2;fontColor=#050505;" vertex="1" parent="1">
<mxGeometry x="460" y="150" width="120" height="30" as="geometry"/>
</mxCell>
<mxCell id="81" value="slot: sidebarBottom&amp;nbsp;&amp;nbsp;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=none;strokeWidth=2;fontColor=#050505;" vertex="1" parent="1">
<mxGeometry x="461" y="380" width="120" height="30" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
1 change: 1 addition & 0 deletions src/documentation/TwoColLayoutWSticky.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
158 changes: 158 additions & 0 deletions src/lib-components/TwoColLayoutWStickySideBar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<script lang="ts" setup>
import { onMounted, ref, watch } from 'vue'
import type { Ref } from 'vue'
import { useWindowSize } from '@vueuse/core'
import SectionWrapper from '@/lib-components/SectionWrapper.vue'
// Track height of sidebar and ensure main content as at least as tall
const isMobile: Ref<boolean> = ref(false)
const sidebar: Ref<HTMLDivElement | null> = ref(null)
const primaryCol: Ref<HTMLDivElement | null> = ref(null)
function setMainMinHeight() {
primaryCol.value!.style!.minHeight = `${sidebar.value!.clientHeight + 125}px` // add 125 px to account for top and bottom padding
}
onMounted(() => {
const { width } = useWindowSize()
watch([width, sidebar], ([newWidth]) => {
isMobile.value = newWidth <= 750
if (isMobile.value === true)
primaryCol.value!.style!.minHeight = 'auto' // on mobile, reset height
else
setMainMinHeight()
}, { immediate: true })
})
</script>

<template>
<div class="two-column">
<!-- main column -->
<div
ref="primaryCol"
class="primary-column top"
>
<SectionWrapper class="primary-section-wrapper">
<slot name="primaryTop" />
<div v-if="isMobile" class="sidebar-mobile-top">
<slot name="sidebarTop" />
</div>
<slot name="primaryMid" />
<div v-if="isMobile" class="sidebar-mobile-bottom">
<slot name="sidebarBottom" />
</div>
<slot name="primaryBottom" />
</SectionWrapper>
</div>

<!-- sidebar column -->
<div class="sidebar-column">
<div
ref="sidebar"
class="sidebar-content-wrapper"
>
<slot v-if="!isMobile" name="sidebarTop" />
<slot v-if="!isMobile" name="sidebarBottom" />
</div>
</div>
</div>
</template>

<style lang="scss" scoped>
.two-column {
position: relative;
width: 100%;
max-width: var(--ftva-container-max-width);
display: flex;
flex-direction: row;
flex-wrap: wrap;
.primary-column {
margin-bottom: 0px;
width: 67%;
.primary-section-wrapper {
padding-left: 0px;
margin: var(--space-2xl) auto;
padding: 0 var(--unit-gutter);
}
}
.sidebar-column {
min-width: 314px;
width: 30%;
position: absolute;
height: 100%;
top: 0;
right: 0;
padding-top: var(--space-2xl);
padding-bottom: 40px;
padding-right: var(--unit-gutter);
.sidebar-content-wrapper {
position: sticky;
top: 85px;
will-change: top;
>* {
margin-bottom: 30px;
&:last-child {
margin-bottom: 48px;
}
}
}
}
}
// MEDIUM DEVICE STYLES
@media (max-width: 1200px) {
.two-column {
padding-right: var(--unit-gutter);
}
.two-column>.primary-column {
width: 62%;
}
}
// MOBILE STYLES
@media #{$small} {
.two-column {
display: grid;
grid-template-columns: 1fr;
.primary-column {
width: auto;
grid-column: 1;
.primary-section-wrapper {
padding-left: var(--unit-gutter);
// Cardmeta divs have a bottom-margin, but the last one doesn't always display if content is overflowing.
// We remove the margin from the last card-meta div to ensure it displays consistently, and re-add the spacing below
:deep(.card-meta) {
div:last-child {
margin-bottom: 0px;
}
}
}
.sidebar-mobile-top {
padding-top: var(--space-l); // add padding to top of sidebar
}
.sidebar-mobile-top, .sidebar-mobile-bottom {
> * {
margin-bottom: 30px;
}
}
}
.sidebar-column {
display: none;
}
}
}
</style>
2 changes: 2 additions & 0 deletions src/lib-components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,5 @@ export { default as StoryWithImage } from './StoryWithImage.vue'
export { default as TabItem } from './TabItem.vue'
export { default as TabList } from './TabList.vue'
export { default as VideoEmbed } from './VideoEmbed.vue'
// LAYOUTS
export { default as TwoColLayoutWStickySideBar } from './TwoColLayoutWStickySideBar.vue' // used in FTVA
10 changes: 10 additions & 0 deletions src/stories/TwoColLayoutStickySideBar.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
describe('LAYOUT / Two Columns W Sticky Sidebar', () => {
it('Default', () => {
cy.visit(
'/iframe.html?id=layout-2-column-layout-with-sticky-sidebar--event-series'
)
cy.get('.two-column').should('exist')

cy.percySnapshot('LAYOUT / Two Columns W Sticky Sidebar: Event Series')
})
})
Loading

1 comment on commit cc5d52d

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.