Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/lib/components/box/Box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ const Box = styled.div.withConfig({
${({ container }) =>
container &&
css`
/* Containment takes the contents out of the intrinsic width, so a container
Box needs a definite width from its parent β€” a flex grow factor, a grid
track or block layout. min-width keeps it from being pinned to the content
width it no longer has; the Box itself stays unopinionated about where the
definite width comes from. */
min-width: 0;
container-type: inline-size;
container-name: responsive;
`}
Expand Down
18 changes: 17 additions & 1 deletion src/lib/components/form/Form.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,33 @@ const StyledForm = styled.form<{
${({ $containerize }) =>
$containerize &&
css`
/* Containment takes the contents out of the intrinsic width, so the width
has to come from the parent: as a content-sized flex item the form would
otherwise resolve to 0 and every child would overflow it. Same pairing as
LeftPanel/RightPanel in layout/v2/panels.tsx. */
flex: 1 1 auto;
min-width: 0;
container-type: inline-size;
container-name: responsive;
`}
`;

// The header, the scroll area and the footer all extend this, so it decides how
// wide a form's content is. 45rem is the page layout's *cap*, not its width β€” a
// pinned width leaves the fields, the section actions and the fixed footer
// outside the page, unreachable, as soon as there is less room than that.
// `width: 100%` needs `box-sizing: border-box`, or the content-box padding
// overflows the parent by exactly that padding. The cap then has to include the
// padding to keep the same 45rem of *content* the pinned width gave.
const PAGE_CONTENT_MAX_WIDTH = '45rem';
const BasicPageLayout = styled.div<{ $layoutKind: 'page' | 'tab' }>`
box-sizing: border-box;
margin: 0 auto;
${(props) =>
props.$layoutKind === 'page'
? `
width: 45rem;
width: 100%;
max-width: calc(${PAGE_CONTENT_MAX_WIDTH} + ${spacing.f16});
padding-right: ${spacing.f16};
`
: `
Expand Down
79 changes: 76 additions & 3 deletions stories/form.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -411,15 +411,23 @@ export const ResponsiveForm = {

// Drag the frame's right edge to narrow the Form. Toggle the `responsive` control
// to compare the two layout modes on the same fields.
const ResizeFrame = ({ children }: { children: React.ReactNode }) => (
const ResizeFrame = ({
children,
minWidth = '16rem',
height = '26rem',
}: {
children: React.ReactNode;
minWidth?: string;
height?: string;
}) => (
<div
style={{
resize: 'horizontal',
overflow: 'auto',
minWidth: '16rem',
minWidth,
maxWidth: '100%',
border: '1px dashed #666',
height: '26rem',
height,
}}
>
{children}
Expand Down Expand Up @@ -885,3 +893,68 @@ export const FormWithAccordion = {
requireMode: 'partial',
},
};

const StepSidebar = () => (
<div
style={{
width: '324px',
flexShrink: 0,
padding: '1rem',
borderRight: '1px solid #666',
}}
>
<Stack direction="vertical" gap="r16">
<Text isEmphazed>Steps</Text>
<Text>1. Connection</Text>
<Text>2. Mapping</Text>
<Text>3. Review</Text>
</Stack>
</div>
);

const ProviderWizardForm = ({ requireMode, responsive }) => (
<Form
layout={{ kind: 'page', title: 'Create provider' }}
requireMode={requireMode}
responsive={responsive}
rightActions={<Button variant="primary" label="Continue" />}
leftActions={<Button variant="outline" label="Cancel" />}
>
<FormSection
title={{ name: 'Connection', icon: 'Node-backend' }}
forceLabelWidth={200}
>
<FormGroup
direction="horizontal"
label="Provider name"
id="wiz-name"
content={<Input id="wiz-name" />}
required
/>
<FormGroup
direction="horizontal"
label="Endpoint"
id="wiz-endpoint"
content={<Input id="wiz-endpoint" />}
help="Optional helper text"
required
/>
</FormSection>
</Form>
);

// A `page` Form in a flex row next to a fixed step sidebar β€” drag the frame's
// right edge down to ~48rem. `responsive` containment removes the form's contents
// from its own intrinsic width, so as a content-sized flex item it needs the width
// to come from the row instead.
export const ResponsivePageFormInFlexRow = {
render: ({ requireMode, responsive }) => (
<ResizeFrame minWidth="30rem" height="32rem">
<div style={{ display: 'flex', height: '100%' }}>
<StepSidebar />
<ProviderWizardForm requireMode={requireMode} responsive={responsive} />
</div>
</ResizeFrame>
),
args: { requireMode: 'partial', responsive: true },
};
Loading