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

Introduce RenderingTarget type and CombinedProps pattern #7359

Merged
merged 4 commits into from
Mar 20, 2023

Conversation

OllysCoding
Copy link
Contributor

Based on the RFC #7174, this PR creates the RenderingTarget type and CombinedProps pattern.

DCR & AR are becoming one - specifically we are adding the capability for DCR to render articles for the app.

We will continue to use the Frontend model for both web & apps articles, and generally web and apps articles will be the same. However, there are some differences in requirements, mainly based on the technical impact of the target:

  • Apps uses native implementations of some features via bridget
  • Apps requires dark mode support
  • & others

To support these differences, this PR introduces the idea of a RenderingTarget, an enum which allows a component or function to know whether the content being rendered will be shown either on the web or the apps.

Additionally, to support differing requirements of data - a rare but nonetheless necessary occurrence - this PR introduces the CombinedProps type & pattern which allows the passing of 'web only' or 'apps only' props, ensuring our type safety is not diluted when rendering for different targets.

Most of the additional details & reasoning are outlined in the RFC: #7174

An important part of this PR is the JSDoc documentation, so please provide feedback of the wording can be improved or better formatted!

@OllysCoding OllysCoding requested a review from a team as a code owner March 8, 2023 10:44
Comment on lines +123 to +124
'no-shadow': 'off', // We use the typescript-eslint version as eslint false positives on enums
'@typescript-eslint/no-shadow': ['error'],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without making this change, we'd getting a variable is already declared in upper scope warning from ESLint, despite that not being the case. Using the typescript version of this rule fixes this problem

Copy link
Contributor

Choose a reason for hiding this comment

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

Was this needed even without the use of TypeScript enums?

@@ -125,10 +125,10 @@ export const FormField = ({
},
]
.concat(formField.options)
.map(({ value, label }) => {
.map(({ value, label: formLabel }) => {
Copy link
Contributor Author

@OllysCoding OllysCoding Mar 8, 2023

Choose a reason for hiding this comment

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

Fix a 'no shadow' error

const nextOffset = offsets
.reverse()
.find((offset) => offset < scrolled);
const nextOffset = offsets.reverse().find((o) => o < scrolled);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fix a 'no shadow' error

Copy link
Contributor

@mxdvl mxdvl Mar 9, 2023

Choose a reason for hiding this comment

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

Could also drop the reverse with Array.prototype.findLast if it’s polyfilled?

Just mentioning as reverse modifies the array in place

@github-actions
Copy link

github-actions bot commented Mar 8, 2023

Size Change: -1.02 kB (0%)

Total Size: 515 kB

Filename Size Change
dotcom-rendering/dist/SetABTests-importable.modern.********************.js 3.91 kB -1.12 kB (-22%) 🎉
ℹ️ View Unchanged
Filename Size Change
dotcom-rendering/dist/116.modern.********************.js 3.84 kB 0 B
dotcom-rendering/dist/1917.modern.********************.js 2.53 kB 0 B
dotcom-rendering/dist/2136.modern.********************.js 3.11 kB 0 B
dotcom-rendering/dist/2153.modern.********************.js 1.89 kB 0 B
dotcom-rendering/dist/3033.modern.********************.js 3.16 kB 0 B
dotcom-rendering/dist/344.modern.********************.js 11.6 kB 0 B
dotcom-rendering/dist/397.modern.********************.js 2.75 kB 0 B
dotcom-rendering/dist/3993.modern.********************.js 6.21 kB 0 B
dotcom-rendering/dist/4180.modern.********************.js 5.13 kB 0 B
dotcom-rendering/dist/4235.modern.********************.js 8.77 kB 0 B
dotcom-rendering/dist/4331.modern.********************.js 3.36 kB 0 B
dotcom-rendering/dist/4730.modern.********************.js 3.61 kB 0 B
dotcom-rendering/dist/4946.modern.********************.js 3.56 kB 0 B
dotcom-rendering/dist/5153.modern.********************.js 2.28 kB 0 B
dotcom-rendering/dist/516.modern.********************.js 17.3 kB 0 B
dotcom-rendering/dist/5237.modern.********************.js 2.44 kB 0 B
dotcom-rendering/dist/6297.modern.********************.js 21.3 kB 0 B
dotcom-rendering/dist/6345.modern.********************.js 4.71 kB 0 B
dotcom-rendering/dist/6429.modern.********************.js 3.5 kB 0 B
dotcom-rendering/dist/6939.modern.********************.js 5.28 kB 0 B
dotcom-rendering/dist/729.modern.********************.js 4.27 kB 0 B
dotcom-rendering/dist/7392.modern.********************.js 2.49 kB 0 B
dotcom-rendering/dist/7679.modern.********************.js 4.36 kB 0 B
dotcom-rendering/dist/7864.modern.********************.js 23.3 kB 0 B
dotcom-rendering/dist/7872.modern.********************.js 1.89 kB 0 B
dotcom-rendering/dist/9861.modern.********************.js 3.5 kB 0 B
dotcom-rendering/dist/AlreadyVisited-importable.modern.********************.js 411 B 0 B
dotcom-rendering/dist/AnimatePulsingDots-importable.modern.********************.js 387 B 0 B
dotcom-rendering/dist/atomIframe.modern.********************.js 513 B 0 B
dotcom-rendering/dist/AudioAtomWrapper-importable.modern.********************.js 466 B 0 B
dotcom-rendering/dist/Branding-importable.modern.********************.js 2.18 kB 0 B
dotcom-rendering/dist/braze-web-sdk-core.modern.********************.js 36.9 kB 0 B
dotcom-rendering/dist/BrazeMessaging-importable.modern.********************.js 4.96 kB 0 B
dotcom-rendering/dist/CalloutBlockComponent-importable.modern.********************.js 6.2 kB +28 B (0%)
dotcom-rendering/dist/CalloutEmbedBlockComponent-importable.modern.********************.js 7.09 kB 0 B
dotcom-rendering/dist/Carousel-importable.modern.********************.js 5.28 kB 0 B
dotcom-rendering/dist/ChartAtomWrapper-importable.modern.********************.js 475 B 0 B
dotcom-rendering/dist/CommentCount-importable.modern.********************.js 2.81 kB 0 B
dotcom-rendering/dist/discussion.modern.********************.js 393 B 0 B
dotcom-rendering/dist/DiscussionContainer-importable.modern.********************.js 4.07 kB 0 B
dotcom-rendering/dist/DiscussionMeta-importable.modern.********************.js 3.35 kB 0 B
dotcom-rendering/dist/DocumentBlockComponent-importable.modern.********************.js 2.72 kB 0 B
dotcom-rendering/dist/EmbedBlockComponent-importable.modern.********************.js 3.25 kB 0 B
dotcom-rendering/dist/embedIframe.modern.********************.js 519 B 0 B
dotcom-rendering/dist/EnhancePinnedPost-importable.modern.********************.js 1.93 kB 0 B
dotcom-rendering/dist/FetchCommentCounts-importable.modern.********************.js 3 kB 0 B
dotcom-rendering/dist/FetchOnwardsData-importable.modern.********************.js 2.52 kB 0 B
dotcom-rendering/dist/FilterKeyEventsToggle-importable.modern.********************.js 3.41 kB 0 B
dotcom-rendering/dist/FocusStyles-importable.modern.********************.js 509 B 0 B
dotcom-rendering/dist/frameworks.modern.********************.js 20.3 kB 0 B
dotcom-rendering/dist/GetCricketScoreboard-importable.modern.********************.js 3.36 kB 0 B
dotcom-rendering/dist/GetMatchNav-importable.modern.********************.js 11.3 kB 0 B
dotcom-rendering/dist/GetMatchStats-importable.modern.********************.js 6.35 kB +49 B (+1%)
dotcom-rendering/dist/GetMatchTabs-importable.modern.********************.js 2.42 kB 0 B
dotcom-rendering/dist/guardian-braze-components-banner.modern.********************.js 11.8 kB 0 B
dotcom-rendering/dist/guardian-braze-components-end-of-article.modern.********************.js 9.34 kB 0 B
dotcom-rendering/dist/GuideAtomWrapper-importable.modern.********************.js 477 B 0 B
dotcom-rendering/dist/HeaderTopBar-importable.modern.********************.js 10.8 kB 0 B
dotcom-rendering/dist/index.modern.********************.js 30.7 kB 0 B
dotcom-rendering/dist/InstagramBlockComponent-importable.modern.********************.js 2.79 kB 0 B
dotcom-rendering/dist/InteractiveBlockComponent-importable.modern.********************.js 5.8 kB 0 B
dotcom-rendering/dist/InteractiveContentsBlockComponent-importable.modern.********************.js 4.08 kB 0 B
dotcom-rendering/dist/InteractiveSupportButton-importable.modern.********************.js 3.81 kB 0 B
dotcom-rendering/dist/KeyEventsCarousel-importable.modern.********************.js 2.94 kB 0 B
dotcom-rendering/dist/KnowledgeQuizAtomWrapper-importable.modern.********************.js 483 B 0 B
dotcom-rendering/dist/LabsHeader-importable.modern.********************.js 2.64 kB 0 B
dotcom-rendering/dist/LiveBlogEpic-importable.modern.********************.js 4.72 kB 0 B
dotcom-rendering/dist/Liveness-importable.modern.********************.js 5.52 kB 0 B
dotcom-rendering/dist/MapEmbedBlockComponent-importable.modern.********************.js 5.32 kB 0 B
dotcom-rendering/dist/Metrics-importable.modern.********************.js 2.23 kB 0 B
dotcom-rendering/dist/MostViewedFooter-importable.modern.********************.js 4.88 kB 0 B
dotcom-rendering/dist/MostViewedFooterData-importable.modern.********************.js 7.05 kB +18 B (0%)
dotcom-rendering/dist/MostViewedRightWrapper-importable.modern.********************.js 3.73 kB 0 B
dotcom-rendering/dist/newsletterEmbedIframe.modern.********************.js 621 B 0 B
dotcom-rendering/dist/OnwardsUpper-importable.modern.********************.js 8.22 kB 0 B
dotcom-rendering/dist/PersonalityQuizAtomWrapper-importable.modern.********************.js 483 B 0 B
dotcom-rendering/dist/ProfileAtomWrapper-importable.modern.********************.js 478 B 0 B
dotcom-rendering/dist/QandaAtomWrapper-importable.modern.********************.js 477 B 0 B
dotcom-rendering/dist/ReaderRevenueDev-importable.modern.********************.js 460 B 0 B
dotcom-rendering/dist/readerRevenueDevUtils.modern.********************.js 2.79 kB 0 B
dotcom-rendering/dist/ReaderRevenueLinks-importable.modern.********************.js 5.41 kB 0 B
dotcom-rendering/dist/RecipeMultiplier-importable.modern.********************.js 3.23 kB 0 B
dotcom-rendering/dist/relativeTime.modern.********************.js 974 B 0 B
dotcom-rendering/dist/RichLinkComponent-importable.modern.********************.js 5.05 kB 0 B
dotcom-rendering/dist/SecureSignupIframe-importable.modern.********************.js 2.54 kB 0 B
dotcom-rendering/dist/sentry.modern.********************.js 767 B 0 B
dotcom-rendering/dist/ShareCount-importable.modern.********************.js 2.94 kB 0 B
dotcom-rendering/dist/shimport.modern.********************.js 2.78 kB 0 B
dotcom-rendering/dist/ShowHideContainers-importable.modern.********************.js 719 B 0 B
dotcom-rendering/dist/ShowMore-importable.modern.********************.js 6.15 kB 0 B
dotcom-rendering/dist/SignInGateMain.modern.********************.js 2.94 kB 0 B
dotcom-rendering/dist/SignInGateMainCheckoutComplete.modern.********************.js 3.84 kB 0 B
dotcom-rendering/dist/SignInGateSelector-importable.modern.********************.js 3.45 kB 0 B
dotcom-rendering/dist/SlotBodyEnd-importable.modern.********************.js 2.79 kB 0 B
dotcom-rendering/dist/Snow-importable.modern.********************.js 4.26 kB 0 B
dotcom-rendering/dist/SpotifyBlockComponent-importable.modern.********************.js 5.17 kB 0 B
dotcom-rendering/dist/StickyBottomBanner-importable.modern.********************.js 3.8 kB 0 B
dotcom-rendering/dist/SubNav-importable.modern.********************.js 2.79 kB 0 B
dotcom-rendering/dist/SupportTheG-importable.modern.********************.js 5.38 kB 0 B
dotcom-rendering/dist/TableOfContents-importable.modern.********************.js 3.07 kB 0 B
dotcom-rendering/dist/TimelineAtomWrapper-importable.modern.********************.js 477 B 0 B
dotcom-rendering/dist/TopRightAdSlot-importable.modern.********************.js 664 B 0 B
dotcom-rendering/dist/TweetBlockComponent-importable.modern.********************.js 999 B 0 B
dotcom-rendering/dist/UnsafeEmbedBlockComponent-importable.modern.********************.js 2.8 kB 0 B
dotcom-rendering/dist/VideoFacebookBlockComponent-importable.modern.********************.js 5.33 kB 0 B
dotcom-rendering/dist/VineBlockComponent-importable.modern.********************.js 2.64 kB 0 B
dotcom-rendering/dist/YoutubeBlockComponent-importable.modern.********************.js 4.05 kB 0 B

compressed-size-action

@github-actions
Copy link

github-actions bot commented Mar 8, 2023

⚡️ Lighthouse report for the changes in this PR

Report for Article

⚠️ Budget exceeded for 1 of 6 audits.

tested url http://localhost:9000/Article/https://www.theguardian.com/commentisfree/2020/feb/08/hungary-now-for-the-new-right-what-venezuela-once-was-for-the-left#noads

Category Status Expected Actual
First Contentful Paint 1500 1204
Largest Contentful Paint 3000 1300
Time to Interactive 3500 1786
Cumulative Layout Shift ⚠️ 0.002 0.015523
Total Blocking Time 219 68
accessibility 0.98 0.980000

@github-actions
Copy link

github-actions bot commented Mar 8, 2023

⚡️ Lighthouse report for the changes in this PR

Report for Front

⚠️ Budget exceeded for 1 of 6 audits.

tested url http://localhost:9000/Front/https://www.theguardian.com/uk

Category Status Expected Actual
First Contentful Paint 1500 1353
Largest Contentful Paint 3000 1956
Time to Interactive 3500 1987
Cumulative Layout Shift ⚠️ 0.002 0.011934
Total Blocking Time 716 373
accessibility 0.98 0.980000

Copy link
Contributor

@JamieB-gu JamieB-gu left a comment

Choose a reason for hiding this comment

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

Nice! Very thorough documentation.


/**
* This type can be set to either AppsProps or WebProps, and takes two generics
* one for each rendering targets props
Copy link
Contributor

@bryophyta bryophyta Mar 9, 2023

Choose a reason for hiding this comment

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

I know that we're not exporting this, but I think it would be useful to elaborate a bit on how this type works? (I think I know, but I'm not super confident on the details, and it feels like something that would be useful for future travellers.)

Copy link
Contributor

@bryophyta bryophyta left a comment

Choose a reason for hiding this comment

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

This looks good to me! I've just added one small comment about documentation 🙂

Copy link
Contributor

@georgeblahblah georgeblahblah left a comment

Choose a reason for hiding this comment

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

Looks good to me! I really appreciated the clear explanations

Comment on lines 1 to 15
/**
* An enum defining what where we are targeting a particular rendered
* page to be shown
*
* This can be used to make decisions during rendering, where there
* might be differences in the requirements of each target.
*
* Targets:
* - Web => A full web browser, such as chrome or safari on a desktop computer, laptop or phone.
* - Apps => A webview rendered within the Android or iOS live apps
*/
export enum RenderingTarget {
Web,
Apps,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

As I mentioned in the RFC, I would recommend avoiding TypeScript enums, as they “are one of the few features TypeScript has which is not a type-level extension of JavaScript”1.

To prove my point, what do you expect the value of RenderingTarget[0] to be? What about RenderingTarget.Web === 0?

Footnotes

  1. https://www.typescriptlang.org/docs/handbook/enums.html

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would your suggestion be to use a string union? What's the best alternative

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, string unions are a lot less confusing. If you need to do custom type-guard, the pattern I shared in the RFC works well:

// If you define a array `as const`…
const renderingTarget = [
    "Web",
    "Apps",
    "Editions",
] as const;

// … you can infer the type from it…
export type RenderingTarget = renderingTarget[number];

// … and create a custom type guard, if you so desire!
export isRenderingTarget = (target: string): target is RenderingTarget =>
	renderingTarget.map(String).includes(target)

Copy link
Contributor

@mxdvl mxdvl left a comment

Choose a reason for hiding this comment

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

This looks like it bring a lot if type safety and is well documented: great!

Should the no-shadow change be extracted out of this PR?

Is the complexity of this wrapper type worth it compared to a naive implementation?

interface CommonProps {
  value: string;
  target: RenderingTarget; // enum or string union
}

interface AppProps extends CommonProps {
  target: "Apps";
  apiUrl: string;
}

interface WebProps extends CommonProps {
  target: "Web";
}

const Component = (props: AppProps | WebProps) =>
  <>{/* … */}</>

@OllysCoding OllysCoding merged commit bc6263a into main Mar 20, 2023
@OllysCoding OllysCoding deleted the olly/rendering-targets branch March 20, 2023 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Establish a way to have varying component props based on the target platform
5 participants