Skip to content

Commit

Permalink
chore: Header responsiveness explorations
Browse files Browse the repository at this point in the history
  • Loading branch information
timogasda committed Jul 28, 2023
1 parent a63348f commit 12995c9
Show file tree
Hide file tree
Showing 2 changed files with 371 additions and 0 deletions.
229 changes: 229 additions & 0 deletions pages/header/responsiveness.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
/* eslint-disable react/jsx-key */
import React, { useRef, useState } from 'react';
import clsx from 'clsx';
import Box from '~components/box';
import Button from '~components/button';
import ButtonDropdown from '~components/button-dropdown';
import Container from '~components/container';
import Header, { HeaderProps } from '~components/header';
import Link from '~components/link';
import SpaceBetween from '~components/space-between';
import ScreenshotArea from '../utils/screenshot-area';
import styles from './styles.scss';
import { useResizeObserver } from '~components/internal/hooks/container-queries';

type Approach = 'current' | 'grid' | 'flex' | 'cquery' | 'resize-observer';

type DemoHeaderProps = Pick<HeaderProps, 'children' | 'actions' | 'description' | 'info'>;

const permutations: Array<Partial<HeaderProps>> = [
{
children: 'Simple container header',
description: (
<span>
Fairly short description for this container. Not much to see here except for{' '}
<Link href="#" variant="secondary">
a link
</Link>
.
</span>
),
actions: <Button>One action</Button>,
},
{
children: 'Tags (4)',
info: <Link variant="info">Info</Link>,
description:
'A tag is a label that you assign to an AWS resource. Each tag consists of a key and an optional value. You can use tags to search and filter your resources or track your AWS costs.',
actions: <Button>Manage tags</Button>,
},
{
children: 'Snapshots',
description: 'Select at least one snapshot to perform an action.',
actions: (
<SpaceBetween direction="horizontal" size="xs">
<Button iconName="refresh" ariaLabel="Refresh snapshots" />
<ButtonDropdown items={[]}>Actions</ButtonDropdown>
<Button variant="primary">Create snapshot</Button>
</SpaceBetween>
),
},
{
children: 'Workgroups',
description:
'Use workgroups to separate users, teams, applications, workloads, and to set limits on amount of data for each query or the entire workgroup process. You can also view query-related metrics in AWS CloudWatch.',
actions: (
<SpaceBetween direction="horizontal" size="xs">
<ButtonDropdown items={[]}>Actions</ButtonDropdown>
<Button variant="primary">Create workgroup</Button>
</SpaceBetween>
),
},
{
children: 'Access Points (100)',
info: <Link variant="info">Info</Link>,
description: (
<span>
Amazon S3 Access Points simplify managing data access at scale for shared datasets in S3. Access points are
named network endpoints that are attached to buckets that you can use to perform S3 object operations. An Access
Point alias provides the same functionality as an Access Point ARN and can be substituted for use anywhere an S3
bucket name is normally used for data access.{' '}
<Link href="#" variant="secondary" external={true} ariaLabel="Learn more about access points">
Learn more
</Link>
</span>
),
actions: (
<SpaceBetween direction="horizontal" size="xs">
<Button iconName="copy">Copy Access Point alias</Button>
<Button iconName="copy">Copy ARN</Button>
<Button>Edit policy</Button>
<Button>Delete</Button>
<Button variant="primary">Create access point</Button>
</SpaceBetween>
),
},
];

function HeaderGrid({ children, actions, description }: DemoHeaderProps) {
return (
<div className={styles['header-grid']}>
<SpaceBetween direction="horizontal" size="xs" className={styles['header-grid--title']}>
<Box variant="h2">{children}</Box>
</SpaceBetween>
<Box variant="p" color="text-body-secondary" className={styles['header-grid--description']}>
{description}
</Box>
<div className={styles['header-grid--actions']}>{actions}</div>
</div>
);
}

function HeaderFlex({ children, actions, description }: DemoHeaderProps) {
return (
<div className={styles['header-flex']}>
<div className={styles['header-flex--heading']}>
<SpaceBetween direction="horizontal" size="xs" className={styles['header-flex--title']}>
<Box variant="h2">{children}</Box>
</SpaceBetween>
<Box variant="p" color="text-body-secondary" className={styles['header-flex--description']}>
{description}
</Box>
</div>
<div className={styles['header-flex--actions']}>{actions}</div>
</div>
);
}

function HeaderContainerQuery({ children, actions, description }: DemoHeaderProps) {
return (
<div className={styles['header-cquery-wrapper']}>
<div className={styles['header-cquery']}>
<Box variant="h2" className={styles['header-cquery--title']}>
{children}
</Box>
<Box variant="p" color="text-body-secondary" className={styles['header-cquery--description']}>
{description}
</Box>
<div className={styles['header-cquery--actions']}>{actions}</div>
</div>
</div>
);
}

function HeaderResizeObserver({ children, actions, description }: DemoHeaderProps) {
const ref = useRef(null);
const titleRef = useRef<HTMLDivElement>(null);
const actionsRef = useRef<HTMLDivElement>(null);
const [actionsOverlap, setActionsOverlap] = useState(false);

useResizeObserver(ref, entry => {
console.log('resize!', entry);
if (titleRef?.current && actionsRef?.current) {
const titleRect = titleRef.current.getBoundingClientRect();
const actionsRect = actionsRef.current.getBoundingClientRect();
const distance = actionsRect.x - (titleRect.x + titleRect.width);

console.log(titleRect, actionsRect);
console.log('distance:', distance);

setActionsOverlap(distance > 50 || distance <= 0);
}
});
return (
<div className={styles['header-grid-wrapper']} ref={ref}>
<div className={clsx(styles['header-grid'], actionsOverlap && styles['header-grid--overlap'])}>
<Box variant="h2" className={styles['header-grid--title']}>
<div ref={titleRef}>{children}</div>
</Box>
<Box variant="p" color="text-body-secondary" className={styles['header-grid--description']}>
{description}
</Box>
<div ref={actionsRef} className={styles['header-grid--actions']}>
{actions}
</div>
</div>
</div>
);
}

function renderHeader(approach: Approach, props: Partial<HeaderProps>) {
switch (approach) {
case 'grid':
return <HeaderGrid {...props} />;
case 'flex':
return <HeaderFlex {...props} />;
case 'cquery':
return <HeaderContainerQuery {...props} />;
case 'resize-observer':
return <HeaderResizeObserver {...props} />;
case 'current':
default:
return <Header {...props} />;
}
}

const approachDescription: Record<Approach, string> = {
current: 'Current implementation',
grid: 'CSS Grid that places actions in the top right',
flex: 'Similar to current approach but with different flows',
cquery: 'Like grid, but rearranges actions when container is small',
'resize-observer': 'not working',
};

function Showcase({ approach }: { approach: Approach }) {
return (
<div className={styles.showcase}>
<div>
<h2>Approach: {approach}</h2>
<p>{approachDescription[approach]}</p>
</div>

{permutations.map((props, i) => {
return (
<Container key={i} header={renderHeader(approach, props)}>
Container content
</Container>
);
})}
</div>
);
}

export default function PageHeadersDemo() {
return (
<ScreenshotArea>
<Box padding="l">
<div className={styles.playground}>
<Showcase approach="current" />
{/* <Showcase approach="resize-observer" /> */}
<Showcase approach="grid" />
<Showcase approach="cquery" />
<Showcase approach="flex" />
</div>
</Box>
</ScreenshotArea>
);
}
142 changes: 142 additions & 0 deletions pages/header/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

/* stylelint-disable */

@use '~design-tokens' as awsui;

.playground {
display: grid;
// grid-template-columns: repeat(auto-fit, minmax(600px, 1fr));
grid-template-columns: repeat(1, 1fr);
column-gap: awsui.$space-scaled-l;
}

.showcase {
display: grid;

grid-template-columns: 1fr;
grid-template-rows: auto;
grid-row: span 6;
row-gap: awsui.$space-scaled-s;
// grid-row: 1 / 100;

grid-template-columns: subgrid;
grid-template-rows: subgrid;
}

///// AUTOFLOW APPROACH (doesnt work well)
// .header-grid {
// display: grid;
// grid-auto-flow: dense;
// grid-auto-columns: 1fr minmax(auto, 75%);
// column-gap: awsui.$space-scaled-xs;
// row-gap: awsui.$space-scaled-xs;

// > .header-grid--description {
// grid-column-end: span 2;
// }
// }

////// GRID AREAS APPROACH
.header-grid {
display: grid;
grid-template-areas:
'title actions'
'description description';
grid-template-columns: max-content 1fr;
column-gap: awsui.$space-scaled-xs;
row-gap: awsui.$space-scaled-xs;

> .header-grid--title {
grid-area: title;
}
> .header-grid--description {
grid-area: description;
}
> .header-grid--actions {
grid-area: actions;
justify-self: flex-end;
}
&.header-grid--overlap {
// For the resize observer approach
// grid-template-areas:
// 'title'
// 'description'
// 'actions';
display: block;
grid-template-columns: 1fr;

> .header-cquery--actions {
justify-self: flex-start;
}
}
}

////// FLEXBOX APPROACH
.header-flex {
display: flex;
flex-wrap: wrap;
justify-content: space-between;

column-gap: awsui.$space-scaled-xs;

container-type: inline-size;
}
.header-flex--heading {
display: flex;
flex-direction: column;
flex-basis: 60%;
flex-grow: 1;
flex-shrink: 1;
}
.header-flex--actions {
display: flex;
align-items: flex-start;
}
@container (max-width: 760px) {
.header-flex--heading {
flex-basis: auto;
}
}

////// CONTAINER QUERY APPROACH
.header-cquery-wrapper {
container-type: inline-size;
}
.header-cquery {
display: grid;
grid-template-areas:
'title actions'
'description description';
grid-template-columns: 1fr auto;
column-gap: awsui.$space-scaled-xs;
row-gap: awsui.$space-scaled-xs;

> .header-cquery--title {
grid-area: title;
}
> .header-cquery--description {
grid-area: description;
}
> .header-cquery--actions {
grid-area: actions;
justify-self: flex-end;
}
}
@container (max-width: 900px) {
.header-cquery {
grid-template-areas:
'title'
'description'
'actions';

> .header-cquery--actions {
justify-self: flex-start;
}
}
}

/* stylelint-enable */

0 comments on commit 12995c9

Please sign in to comment.