Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ResponsiveLayout): nested ResponsiveLayout background takes precedence #909

Merged
merged 13 commits into from
Oct 25, 2023
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions src/__screenshot_tests__/responsive-layout-screenshot-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {openStoryPage} from '../test-utils';

const TEST_CASES = [
{id: 'layout-responsive-layout--default', device: 'MOBILE_IOS'},
{id: 'layout-responsive-layout--default', device: 'MOBILE_ANDROID'},
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd remove one of these. Testing iOS/Android doesn't give any value in this component, the relevant thing here is the screen size. Having different tests for Android/iOS makes sense in components that change betwen platforms, like the Switch

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok !! fue un copy paste leftover XD

{id: 'layout-responsive-layout--default', device: 'TABLET'},
{id: 'layout-responsive-layout--default', device: 'DESKTOP'},
{id: 'layout-responsive-layout--default', device: 'DESKTOP', viewport: {width: 1368, height: 770}},
{id: 'layout-responsive-layout--nested', device: 'MOBILE_IOS'},
{id: 'layout-responsive-layout--nested', device: 'MOBILE_ANDROID'},
Copy link
Contributor

Choose a reason for hiding this comment

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

remove this one too

{id: 'layout-responsive-layout--nested', device: 'TABLET'},
{id: 'layout-responsive-layout--nested', device: 'DESKTOP'},
{id: 'layout-responsive-layout--nested', device: 'DESKTOP', viewport: {width: 1368, height: 770}},
] as const;

test.each(TEST_CASES)('ResponsiveLayout', async (testCase) => {
await openStoryPage({
...testCase,
});

const image = await page.screenshot();
expect(image).toMatchImageSnapshot();
});
14 changes: 14 additions & 0 deletions src/__stories__/responsive-layout-story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,17 @@ Default.args = {
isInverse: false,
withBackgroundColor: false,
};

export const Nested: StoryComponent = () => (
<ResponsiveLayout variant="alternative">
<Placeholder />
<ResponsiveLayout variant="inverse">
<Placeholder />
<ResponsiveLayout variant="default" backgroundColor="gray">
<Placeholder />
</ResponsiveLayout>
</ResponsiveLayout>
</ResponsiveLayout>
);

Nested.storyName = 'Nested responsive layouts';
7 changes: 6 additions & 1 deletion src/carousel.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,12 @@ export const carousel = style([
const responsiveLayoutSideMargin = fallbackVar(responsiveLayoutVars.sideMargin, '0px');

export const carouselWithScroll = style({
margin: `0 calc(${responsiveLayoutSideMargin} * -1)`,
margin: 0,
'@media': {
[mq.tabletOrSmaller]: {
margin: `0 calc(${responsiveLayoutSideMargin} * -1)`,
},
},
});

export const centeredCarousel = style({
Expand Down
17 changes: 12 additions & 5 deletions src/dialog.css.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import {style, keyframes, styleVariants} from '@vanilla-extract/css';
import {style, keyframes, styleVariants, createVar} from '@vanilla-extract/css';
import * as mq from './media-queries.css';
import {vars} from './skins/skin-contract.css';
import {vars as skinVars} from './skins/skin-contract.css';
import {sprinkles} from './sprinkles.css';
import {pxToRem} from './utils/css';

const insideDialog = createVar();
export const vars = {insideDialog};

const fadeIn = keyframes({
'0%': {opacity: 0},
'100%': {opacity: 1},
Expand Down Expand Up @@ -43,7 +46,7 @@ export const modalOpacityLayer = style([
right: 0,
bottom: 0,
left: 0,
background: vars.colors.backgroundOverlay,
background: skinVars.colors.backgroundOverlay,
}),
{
zIndex: Z_INDEX,
Expand Down Expand Up @@ -72,8 +75,8 @@ export const modalCloseButtonContainer = style([

export const modalContent = style([
sprinkles({
background: vars.colors.background,
borderRadius: vars.borderRadii.container,
background: skinVars.colors.background,
borderRadius: skinVars.borderRadii.container,
}),
{
animation: `${fadeScale} .2s ease-in-out`,
Expand All @@ -91,6 +94,10 @@ const dialogContainer = style([
justifyContent: 'space-between',
}),
{
vars: {
[insideDialog]: '1',
},

width: 'calc(100vw - 48px)',
margin: 'auto',

Expand Down
6 changes: 5 additions & 1 deletion src/fixed-footer-layout.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ import * as mq from './media-queries.css';

const footerHeight = createVar();
const backgroundColor = createVar();
const insideFixedFooter = createVar();

export const vars = {footerHeight, backgroundColor};
export const vars = {footerHeight, backgroundColor, insideFixedFooter};

export const footer = style([
sprinkles({
width: '100%',
background: skinVars.colors.background,
}),
{
vars: {
[insideFixedFooter]: '1',
},
transition: 'box-shadow 0.2s linear',
},
]);
Expand Down
2 changes: 1 addition & 1 deletion src/hero.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import {createVar, style} from '@vanilla-extract/css';
import * as mq from './media-queries.css';
import {sprinkles} from './sprinkles.css';
import {
LARGE_DESKTOP_MAX_WIDTH,
MOBILE_SIDE_MARGIN,
SMALL_DESKTOP_SIDE_MARGIN,
TABLET_SIDE_MARGIN,
LARGE_DESKTOP_MAX_WIDTH,
} from './responsive-layout.css';

const height = createVar();
Expand Down
105 changes: 72 additions & 33 deletions src/responsive-layout.css.ts
Copy link
Member

@pladaria pladaria Oct 18, 2023

Choose a reason for hiding this comment

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

I'm sorry but I don't like this. These are a bunch of workarounds to allow compositions that should be forbidden.

ResponsiveLayouts shouldn't be nested. This is not an issue of the component, it is a problem from the part that uses this component to compose pages or sections in unexpected ways.

If I understand this PR, now you can place ResponsiveLayouts inside dialogs, sheets, footers, other ResponsiveLayouts... It makes no sense to me.

Perhaps the modules shouldn't use a ResponsiveLayout and it should be added by the piece that composes those modules when needed. I guess I'm missing a lot of context about your needs and your implementation details.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree, I don't like nested ResponsiveLayouts either, and I think we should found a way to avoid that. But this PR is not about that, we already supported nested ResponsiveLayouts but we had a bug with background color that this PR fixes.

If we want to discuss a better approach for modular pages in novum webapp or other use cases where we are nesting ResponsiveLayouts, ok. But I'd keep that discussion outside of this PR

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, its another discussion, we can talk about that. This PR gets enmarronated because the large desktop extra margins, this makes everything worse, if we had another component, or a prop to specify that we should add these extra magins, the component would be really small and simple.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I understand this PR, now you can place ResponsiveLayouts inside dialogs, sheets, footers, other ResponsiveLayouts... It makes no sense to me.

This is because of the large desktop extra margins :(

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Implemented the props approach to fix the button fixed footer layout bug.

Original file line number Diff line number Diff line change
@@ -1,60 +1,99 @@
import {createVar, style} from '@vanilla-extract/css';
import {createVar, fallbackVar, style} from '@vanilla-extract/css';
import * as mq from './media-queries.css';
import {sprinkles} from './sprinkles.css';
import {vars as skinVars} from './skins/skin-contract.css';
import {vars as dialogVars} from './dialog.css';
import {vars as sheetVars} from './sheet.css';
import {vars as fixedFooterVars} from './fixed-footer-layout.css';

export const MOBILE_SIDE_MARGIN = 16;
export const TABLET_SIDE_MARGIN = 32;
export const SMALL_DESKTOP_SIDE_MARGIN = 40;
export const LARGE_DESKTOP_MAX_WIDTH = 1224;
Copy link
Contributor

Choose a reason for hiding this comment

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

you have broken the large desktop max width:

try this snippet in master and in your branch playroom:

<ResponsiveLayout>
  <Placeholder/>
</ResponsiveLayout>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I'll check, and add a test for this case, to better cover this situation.

Copy link
Contributor

Choose a reason for hiding this comment

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

yes it would be nice to have some screenshot tests for these cases too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@pladaria this !!


const sideMargin = createVar();
const insideDialog = createVar();
const notInsideDialog = createVar();
const notInsideFixedFooter = createVar();
export const vars = {sideMargin};

export const container = sprinkles({width: '100%'});
export const responsiveLayoutContainer = style([
sprinkles({width: '100%'}),
{
vars: {
[sideMargin]: '0px',
[insideDialog]: `${fallbackVar(dialogVars.insideDialog, sheetVars.insideSheetDialog, '0')}`,
[notInsideDialog]: `(1 - ${fallbackVar(
dialogVars.insideDialog,
sheetVars.insideSheetDialog,
'0'
)})`,
[notInsideFixedFooter]: `(1 - ${fallbackVar(fixedFooterVars.insideFixedFooter, '0')})`,
},
'@media': {
[mq.desktop]: {
vars: {
[sideMargin]: `${SMALL_DESKTOP_SIDE_MARGIN}px`,
},
},
[mq.tablet]: {
vars: {
[sideMargin]: `${TABLET_SIDE_MARGIN}px`,
},
},
[mq.mobile]: {
vars: {
[sideMargin]: `${MOBILE_SIDE_MARGIN}px`,
},
},
},

selectors: {
'& &': {
Copy link
Contributor

Choose a reason for hiding this comment

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

This causes a bug, that is already broken in master: if you place a <ButtonFixedFooterLayout> inside a <ResponsiveLayout> the footer paddings are broken because the internal <ResponsiveLayout> stops working.

I've detected some cases in webapp, where some developers are trying to workaround it by adding their own paddings by hand 🤦 because they don't know the problem is the nested <ResponsiveLayout>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, I'll try to fix that ;)

Copy link
Contributor Author

@wyunreal wyunreal Oct 11, 2023

Choose a reason for hiding this comment

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

Can you please post here a code snippet for the playroom that reproduces the issue ?

Copy link
Contributor

Copy link
Contributor

Choose a reason for hiding this comment

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

@atabel is it the same problem with sheets?
https://tinyurl.com/yqdcvsuf

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've fixed using couple css variables, I don't know if there is any better option, any advice welcome.

Copy link
Contributor

Choose a reason for hiding this comment

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

hmmm... I think the css variables approach is too convoluted. I'd prefer a prop in <ResponsiveLayout> to disable the special nesting behavior in the cases we know we don't want it (fixedFooter, dialogs, etc). If we don't want to make that prop public we could have an InternalResponsiveLayout that accepts that prop, ResponsiveLayout would just be a wrapper of InternalResponsiveLayout without the additional prop

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that was muy preferred solution, but I tried to keep the component's interface unchanged. so What do you prefer @atabel , @pladaria ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would go for a different component, for internal responsive layout, becuase it wont needs to handle large desktop extra margins, for example (that makes everything lot complex).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done !

width: 'auto',
margin: `0 calc(-1 * ${notInsideFixedFooter} * ${sideMargin})`,
'@media': {
[mq.desktop]: {
margin: `0 calc(-1 * ${sideMargin})`,
},
Copy link
Contributor

Choose a reason for hiding this comment

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

isn't this mq redundant? it's like the default case

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oooh yes, this is a leftover, thanks !!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed

[mq.largeDesktop]: {
margin: `0 calc(-1 * (
${notInsideDialog} * (100vw - ${LARGE_DESKTOP_MAX_WIDTH}px) / 2 +
${insideDialog} * ${SMALL_DESKTOP_SIDE_MARGIN}px
))`,
},
},
},
},
},
]);

export const fullwidthContainer = style([
{
vars: {
[sideMargin]: '0px',
},
},
]);

export const backgroundVariant = {
inverse: sprinkles({background: skinVars.colors.backgroundBrand}),
alternative: sprinkles({background: skinVars.colors.backgroundAlternative}),
};

export const responsiveLayout = style({
margin: 'auto',
paddingLeft: 'env(safe-area-inset-left)',
paddingRight: 'env(safe-area-inset-right)',

vars: {
[sideMargin]: '0px',
},

margin: `0 ${sideMargin}`,
'@media': {
[mq.largeDesktop]: {
width: LARGE_DESKTOP_MAX_WIDTH,
maxWidth: `calc(100% - ${SMALL_DESKTOP_SIDE_MARGIN * 2}px)`, // to make ResponsiveLayout work inside desktop modals
},
[mq.desktop]: {
margin: `0 ${SMALL_DESKTOP_SIDE_MARGIN}px`,
},
[mq.tablet]: {
margin: `0 ${TABLET_SIDE_MARGIN}px`,

vars: {
[sideMargin]: `${TABLET_SIDE_MARGIN}px`,
},
},
[mq.mobile]: {
margin: `0 ${MOBILE_SIDE_MARGIN}px`,

vars: {
[sideMargin]: `${MOBILE_SIDE_MARGIN}px`,
},
},
},

selectors: {
'& &': {
margin: 0,
width: 'auto',
margin: `0 calc(
${notInsideDialog} * (100vw - ${LARGE_DESKTOP_MAX_WIDTH}px) / 2 +
${insideDialog} * ${SMALL_DESKTOP_SIDE_MARGIN}px
)`,
width: `calc(${notInsideDialog} * ${LARGE_DESKTOP_MAX_WIDTH}px)`,
minWidth: `calc(${insideDialog} * (100% - ${SMALL_DESKTOP_SIDE_MARGIN * 2}px))`,
},
},
});
Expand Down
2 changes: 1 addition & 1 deletion src/responsive-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const ResponsiveLayout: React.FC<Props> = ({
<ThemeVariant variant={internalVariant ?? outsideVariant}>
<div
className={classnames(
styles.container,
fullWidth ? styles.fullwidthContainer : styles.responsiveLayoutContainer,
className,
internalVariant &&
internalVariant !== 'default' &&
Expand Down
9 changes: 8 additions & 1 deletion src/sheet.css.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import {keyframes, style} from '@vanilla-extract/css';
import {createVar, keyframes, style} from '@vanilla-extract/css';
import * as mq from './media-queries.css';
import {vars as skinVars} from './skins/skin-contract.css';
import {sprinkles} from './sprinkles.css';

const insideSheetDialog = createVar();
export const vars = {insideSheetDialog};

export const transitionDuration = process.env.NODE_ENV === 'test' ? 0 : 400;

const sheetClosedStyle = {
Expand Down Expand Up @@ -41,6 +44,10 @@ export const SheetContainer = style([

'@media': {
[mq.desktopOrBigger]: {
vars: {
[insideSheetDialog]: '1',
},

pointerEvents: 'none', // allow clicks to go through this layer and hit the overlay
top: 0,
display: 'flex',
Expand Down