Skip to content

Conversation

@haroun
Copy link
Contributor

@haroun haroun commented Sep 9, 2024

Summary

Add mobile preview feature to the admin UI
image

What are the specific steps to test this change?

https://preview.a3.apos.dev/

or

  1. Enable mobile preview using asset module devicePreviewMode option in your project
     // module/@apostrophecms/asset/index.js
     module.exports = {
       options: {
         devicePreviewMode: {
           enable: true,
           debug: true,
           resizable: false,
           screens: {
             desktop: {
               label: 'apostrophe:devicePreviewDesktop',
               width: '1500px',
               height: '900px',
               icon: 'monitor-icon'
             },
             tablet: {
               label: 'apostrophe:devicePreviewTablet',
               width: '1024px',
               height: '768px',
               icon: 'tablet-icon'
             },
             mobile: {
               label: 'apostrophe:devicePreviewMobile',
               width: '480px',
               height: '1000px',
               icon: 'cellphone-icon'
             }
           }
         },
         // Transform method used on media feature
         // Can be either:
         // - (mediaFeature) => { return mediaFeature; }
         // - null
         transform: null
       }
     };
  2. Check the display (you can add some media queries to your asset to test the rendering)
    // to put in an scss file or a vue component
    @media (max-width: 1400px) {
      [data-apos-refreshable] main h1 {
        background-color: gold;
      }
    }
    
    @media (max-width: 1200px) {
      [data-apos-refreshable] main h1 {
        background-color: green;
      }
    }
    
    @media (max-width: 800px) {
      [data-apos-refreshable] main h1 {
        background-color: blue;
      }
    }
    
    @media (max-width: 600px) {
      [data-apos-refreshable] main h1 {
        background-color: magenta;
      }
    }
    
    @media (max-width: 400px) {
      [data-apos-refreshable] main h1 {
        background-color: red;
      }
    }

What kind of change does this PR introduce?

(Check at least one)

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • Build-related changes
  • Other

Make sure the PR fulfills these requirements:

  • It includes a) the existing issue ID being resolved, b) a convincing reason for adding this feature, or c) a clear description of the bug it resolves
  • The changelog is updated
  • Related documentation has been updated
  • Related tests have been updated

If adding a new feature without an already open issue, it's best to open a feature request issue first and wait for approval before working on it.

Other information:

@haroun haroun self-assigned this Sep 9, 2024
@linear
Copy link

linear bot commented Sep 9, 2024

* main:
  PRO-6578: auto import of inline images (#4723)
  Inline array improvements (#4671)
  take credit for the fix as well as the "change" (#4725)
  PRO-6576 fix default values for object fields in widgets and generally unify newInstance implementations (#4722)
sourceMap: true
}
}
mediaToContainerQueriesLoader,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@haroun haroun marked this pull request as ready for review September 12, 2024 17:02
@haroun haroun requested a review from BoDonkey September 13, 2024 09:19
* main:
  upgrate stylelint-config-apostrophe to 4.2.0 (#4735)
  PRO-6472: at startup, automatically supply a value for any new schema property that has a def or fallback def (#4721)
  fix sass warning (#4730)
  fix piecesFilters with dynamic choices (#4731)
* main:
  fix oversights in add missing schema fields (#4736)

const containerQuery = convertToContainerQuery(mediaFeature, content);

return `${containerQuery} ${match}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return `${containerQuery} ${match}`;
return `${containerQuery}\n\n${match}`;

(approximative code)

to place the media query on a new line

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 also put the container query after the media query

Copy link
Contributor

@ETLaurent ETLaurent left a comment

Choose a reason for hiding this comment

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

Awesome work 😮
And no iframe needed in the end!

A few comments, but the approval is very close.

height: screen.height
}
},
shortcut: `P,${index}`
Copy link
Contributor

Choose a reason for hiding this comment

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

P,[n] is a bit slow if we want to quickly switch between modes.
But it's not that bad.

export default {
name: 'TheAposContextDevicePreview',
props: {
// { screenName: { label: string, width: string, height: string, icon: string } }
Copy link
Contributor

Choose a reason for hiding this comment

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

wanted comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yep, to explain the expected props

Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a good reason not to use Vue 3 here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

to do like the existing vue components in the project

}) {
document.querySelector('body').setAttribute('data-device-preview-mode', mode);
document.querySelector('[data-apos-refreshable]').setAttribute('data-resizable', this.resizable);
document.querySelector('[data-apos-refreshable]').setAttribute('data-label', this.$t(label));
Copy link
Contributor

Choose a reason for hiding this comment

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

if we're not displaying it anymore then we can remove this line, as well as in toggleDevicePreviewMode and saveStatemethods.

default: false
}
},
emits: [ 'switch-device-preview-mode', 'reset-device-preview-mode' ],
Copy link
Contributor

Choose a reason for hiding this comment

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

I can't find where these are being listened to?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's not, but if you want to listen to it and do something you can

Comment on lines 82 to 85
// Transform method used on media feature
// Can be either:
// - (mediaFeature) => { return mediaFeature; }
// - null
Copy link
Contributor

Choose a reason for hiding this comment

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

This could be enriched with another example which would illustrate why some project would need to do this. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

my plan is to do it in the doc


const mediaQueryRegex = /@media\s*(all|print|screen(?: and | or )?)?([^{]+)[^{]*{([\s\S]*?})\s*(\\n)*}/g;

const convertToContainerQuery = (mediaFeature, content) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I used to be keenly against it (@ValJed would remember) but now, I think that I like having the functions definition placed after their call. I find it much more easier to read in the file (main function -> call to another function -> function definition).

I.e: you could move this function at the end, so we read the code execution in the execution order.
(this is very optional).

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 really have a hard time with hoisting

Copy link
Contributor Author

Choose a reason for hiding this comment

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

and the entire file is 80+ lines of code

Comment on lines 69 to 83
const root = postcss.parse(match.replaceAll(/\\[frntv]/g, ''));
root.walkRules(rule => {
const newRule = rule.clone();
newRule.selectors = newRule.selectors.map(selector => {
if (selector.startsWith('body')) {
return selector.replace('body', 'body:not([data-device-preview-mode])');
}

return `body:not([data-device-preview-mode]) ${selector}`;
});

rule.replaceWith(newRule);
});

return `${root.toString()}\\n\\n${containerQuery}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

beautiful 🥲

Comment on lines 71 to 80
const newRule = rule.clone();
newRule.selectors = newRule.selectors.map(selector => {
if (selector.startsWith('body')) {
return selector.replace('body', 'body:not([data-device-preview-mode])');
}

return `body:not([data-device-preview-mode]) ${selector}`;
});

rule.replaceWith(newRule);
Copy link
Contributor

Choose a reason for hiding this comment

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

we could add a comment to briefly explain why we have to disable rules in media queries when in device preview mode.

Comment on lines 23 to 32
[data-apos-refreshable]::before {
@include type-help;

& {
content: attr(data-label);
position: relative;
top: -1.25rem;
display: block;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

are we still displaying it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not visible but this is a bug

@haroun haroun requested a review from ETLaurent September 26, 2024 15:26
ETLaurent
ETLaurent previously approved these changes Sep 30, 2024
}

return `body:not([data-device-preview-mode]) ${selector}`;
return `:not([data-device-preview-mode]) ${selector}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

I tried this solution (just having :not without body) but it did not work. Does it work on Chrome on your end?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed not a good idea to remove body, it's fixed

@haroun haroun requested a review from ETLaurent October 1, 2024 07:50
ETLaurent
ETLaurent previously approved these changes Oct 1, 2024
rule.replaceWith(newRule);
});

root.append(containerAtRule);
Copy link
Contributor

Choose a reason for hiding this comment

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

much better indeed

const containerAtRule = atRule.clone({
name: 'container',
params: convertToContainerQuery(atRule.params)
.replaceAll(/(only\s*)?(all|screen|print)(,)?(\s)*(and\s*)?/g, '')
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
.replaceAll(/(only\s*)?(all|screen|print)(,)?(\s)*(and\s*)?/g, '')
.replaceAll(/(only\s*)?(all|screen|print),?\s*(and\s*)?/g, '')

};
const options = this.getOptions(schema);

const mediaQueryRegex = /@media[^{]*{([\s\S]*?})\s*(\\n)*}/g;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const mediaQueryRegex = /@media[^{]*{([\s\S]*?})\s*(\\n)*}/g;
const mediaQueryRegex = /@media[^{]*{[\s\S]*}\s*(\\n)*}/g;

no need for parenthesis and question mark (* does the ?'s "job"), I think

Copy link
Contributor Author

Choose a reason for hiding this comment

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

tried and the parentheses are needed, otherwise it will select extra content

@haroun haroun dismissed stuartromanek’s stale review October 2, 2024 13:36

changes have been made already, we have 2 approvals

@haroun haroun merged commit 0f54720 into main Oct 2, 2024
8 checks passed
@haroun haroun deleted the pro-6518-mobile-preview branch October 2, 2024 13:36
haroun added a commit to haroun/apostrophe that referenced this pull request Oct 8, 2024
* upstream/main:
  Release 4.8.0 (apostrophecms#4742)
  Enable by default, other tweaks (apostrophecms#4741)
  PRO-6627: Fix array reorder and labels (apostrophecms#4740)
  Pro 6518 mobile preview (apostrophecms#4720)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants