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
✨[amp-social-share][bento][preact] New version of preact social-share component #28137
Conversation
const size = { | ||
width: props['width'] || DEFAULT_WIDTH, | ||
height: props['height'] || DEFAULT_HEIGHT, | ||
width: checkedWidth, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's floor these instead of checking if they're integers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
combinedBindings[name.toUpperCase()] = bindings[name] | ||
? bindings[name] | ||
: ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
combinedBindings[name.toUpperCase()] = bindings[name] | |
? bindings[name] | |
: ''; | |
combinedBindings[name.toUpperCase()] = bindings[name] ?? ''; |
const bindingVars = typeConfig['bindings']; | ||
if (bindingVars) { | ||
bindingVars.forEach((name) => { | ||
combinedBindings[name.toUpperCase()] = params[name] ? params[name] : ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
combinedBindings[name.toUpperCase()] = params[name] ? params[name] : ''; | |
combinedBindings[name.toUpperCase()] = params[name] ?? ''; |
const combinedParams = dict(); | ||
Object.assign(combinedParams, typeConfig['defaultParams'], params); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the linter complain using this form?
const combinedParams = dict(); | |
Object.assign(combinedParams, typeConfig['defaultParams'], params); | |
const combinedParams = dict({...typeConfig['defaultParams'], ...params}); |
} | ||
|
||
// Throw warning if type is obsolete | ||
const obsoleteType = typeConfig['obsolete']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the conditions that lead to an obsolete
type?
Since this is a new version of the component, let's get rid of types we don't support if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's only one obsolete type, it's 'gplus'.
Yes, we can just remove support for this type and remove this functionality. It's included here: https://github.com/ampproject/amphtml/blob/master/extensions/amp-social-share/0.2/amp-social-share-config.js
So wasn't sure if there might be a future need for this type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, let's remove :)
@@ -30,63 +32,196 @@ const DEFAULT_HEIGHT = 44; | |||
* @param {!JsonObject} props | |||
* @return {PreactDef.Renderable} | |||
*/ | |||
export function SocialShare(props) { | |||
export function SocialShare2(props) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"2"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the name, because I was concerned that I would break the amp component. The old version of the preact component has different behavior, and the amp component relies on the old version. Should I remove the 2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Let's break.
event.preventDefault(); | ||
handleActivation(); | ||
function checkProps_(props, name) { | ||
const {type, shareEndpoint, params, bindings, width, height} = props; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, very unfortunately, these would have to be defined with quotes. E.g.
const {'type': type, ...} = props;
if (key == Keys.SPACE || key == Keys.ENTER) { | ||
event.preventDefault(); | ||
handleActivation(); | ||
function checkProps_(props, name) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a static function? If so, let's make it a module-level function. Also, let's drop _
for this and others.
|
||
// Verify type is valid and cannot contain spaces | ||
if (type === undefined) { | ||
throwError_(`The type attribute is required. ${name}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just do throw new Error()
here? If we actually throw errors, it'd be better to be explicit here, since the compiler understands that the function exits with the throw
statement.
But, I'm not yet sold we should be throwing errors here. I think they are somewhat unidiomatic in React development. But let's do it for now, until we figure out a better way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I wrapped the actual throwing of an error inside it's own function in case we want to change it throughout the component, since I am not sure what is the idiomatic way to throw an error in React and specifically for bento.
Sure I can make it explicit here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We just need to be consistent here. If it's a method call that may or may not throw in different cases, we have to propagate null/undefined
values, and then skip rendering by returning null. If we throw
errors and don't want to deal with this yet, we should make throw
explicit to compiler and the only way to do it is to actually have throw ...
at the call site. I recommend we start by throwing actual errors explicitly - it's less effort for us for now and we'd likely have to pass through the errors again at some point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, let's throw the error explicitly.
throwError_(`The type attribute is required. ${name}`); | ||
} | ||
if (/\s/.test(type)) { | ||
throwError_( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This error seems way to detailed and too obscure. Is there a specific reason why we should worry about spaces here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a carry over from the previous version of social-share. I wanted to keep the behavior the same. I can remove if desired.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed!
function handleActivation_() { | ||
if (finalEndpoint.split(':', 1)[0] === 'navigator-share') { | ||
if (window && window.navigator && window.navigator.share) { | ||
const dataStr = finalEndpoint.substr(finalEndpoint.indexOf('?')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be a util for this in the url.js
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like there is getFragment() which gets params after '#' symbol but nothing for '?' unless I'm missing somehting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's parseUrl().queryString
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though I'm not strictly against doing URL splitting here directly. But then we need to handle edge-cases, such as what if there's also a #
in this URL, what if there's no ?
, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the calling this out. I was only able to find parseUrlDeprecated which I am hesitant to use because of the Deprecation. I have attempted to write my own helper function (getQueryString, line 192), which handles the corner cases that I can think of.
- No
?
-> returns '' (empty string) ?
and#
-> only returns the substring from?
to#
#
then?
(reversed order) -> returns substring from?
to the end of string?
and no#
-> returns substring from?
to the end of string
const backgroundStyle = CSS[type]; | ||
//Default to gray (d3d3d3) background if type is not preconfigured | ||
const backgroundStyle = CSS[type.toUpperCase()] || { | ||
'backgroundColor': 'd3d3d3', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should have #
in front?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll consolidate into CSS file to make it easier to understand. The current formatting does not require #.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are not CSS values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is a CSS value, but this is the format that the component consumes it in, (does not require the #
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. I don't like it. But I guess let's run with what they accept and we'll see if that causes problems later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good!
style={{...size, ...props['style']}} | ||
{...props} | ||
> | ||
<SocialShareIcon | ||
style={{...backgroundStyle, ...baseStyle, ...size}} | ||
type={type} | ||
type={type.toUpperCase()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's get the type.toUpperCase()
value once somewhere up top and use it elsewhere.
import {withA11y} from '@storybook/addon-a11y'; | ||
|
||
export default { | ||
title: 'Social Share 2', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0.2
Thanks for review @alanorozco @dvoytenko . Next revision is up! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking forward to see how this impacts AMP mode!
@@ -54,3 +54,6 @@ export const SMS = {'backgroundColor': 'ca2b63'}; | |||
|
|||
/* "system" styling */ | |||
export const SYSTEM = {'backgroundColor': '000000'}; | |||
|
|||
/* "default" styling */ | |||
export const DEFAULT = {'backgroundColor': 'd3d3d3'}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default color is currently white, which is invisible in most cases against a white background (including in storybook). So I felt it made sense to explicitly set some visible color.
const endpointWithParams = addParamsToUrl(baseEndpoint, combinedParams); | ||
|
||
const combinedBindings = dict(); | ||
const bindingVars = typeConfig['bindings']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are examples of the bindings right? https://github.com/ampproject/amphtml/blob/master/spec/amp-var-substitutions.md Are they useful outside of an AMP document?
} | ||
|
||
// Verify width and height are valid integers | ||
let checkedWidth = Math.floor(width || DEFAULT_WIDTH); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an interesting case: Math.floor(null)
yields 0
but Math.floor(undefined)
yields NaN
-- if the prop isn't present it's undefined
, meaning it would be reasonable to rewrite this: Math.floor(width) || DEFAULT_WIDTH
Do we ever expect width
to be null
? That would be a reasonable case for the current order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edit: Just saw the positive integer check below, in which case even if it is null
we would prefer to use the DEFAULT_WIDTH
.
tldr supernit: Math.floor(width) || DEFAULT_WIDTH
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh thanks for the input. Right, I agree there are many tricky corner cases for this. I've included your input in the new version which now follows the below behavior!
// Verify width and height are valid integers
// Silently assigns default for undefined, null
// Throws Warning for booleans, strings, 0, negative numbers
// No errors when positive integer or equivalent string
// Verify width and height are valid integers | ||
let checkedWidth = Math.floor(width || DEFAULT_WIDTH); | ||
let checkedHeight = Math.floor(height || DEFAULT_HEIGHT); | ||
if (!(checkedWidth > 0)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: checkedWidth <= 0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I've re-written this seciton, although kept this part with > 0
because when Math.floor returns NaN, it always return false in a comparison so the recommended update would return a different behavior.
); | ||
checkedWidth = DEFAULT_WIDTH; | ||
} | ||
if (!(checkedHeight > 0)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checkedHeight <= 0
Thanks again for the review @caroqliu @dvoytenko @alanorozco I've made all the changes besides the ones for params / bindings. My proposal for next steps is the following
Let me know what you guys think! Also I moved everything into a directory called 1.0, but this makes it hard to diff. Let me know if you want me to move it back to 0.2 and then make the 1.0 change in a separate PR. Here's my latest thoughts on the params / bindings discussion. #28137 (comment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few pre-LGTM comments.
/** | ||
* @param {!JsonObject} props | ||
* @param {?string} name | ||
* @return {!JsonObject} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should return a struct type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated!
useResourcesNotify(); | ||
|
||
const { | ||
'typeConfig': typeConfig, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The checkProps
should return a struct type and no quotes should be used here. E.g.
const {baseEndpoint, ...} = checkProps(props);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I will make the change. Per your previous comment: #28137 (comment)
Is the correct convention to use quotes when unpacking the props only? And then for unpacking any other object do not use quotes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. For now this is the convention. Any internal structure should have a type declaration that could be a struct. Then we don't need quotes. Unfortunately we can't control a value that's passed to us from outside, such as component props. Another way to think about it is obfuscation. I.e. do we want an obfuscation for any structure? We can't obfuscate component props or component output. For instance, one of such props could be disabled
. If we obfuscate it to d
and output it to JSX - a DOM element expects disabled
. But the structures you use internally are ok to obfuscate. E.g. typeConfig
will become tc
or such. But it's not a visible value to anyone but our internal code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. Thanks for clarifying how the obfuscater interacts with properties passed from outside the component. I have removed the quotes here, and included quotes inside of checkProps where the props are unpacked.
* @return {PreactDef.Renderable} | ||
*/ | ||
export function SocialShare(props) { | ||
const name = 'SocialShare'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make a top-level const.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated!
* @param {!JsonObject} props | ||
* @return {?string} | ||
*/ | ||
function createEndpoint(typeConfig, baseEndpoint, props) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move to a top-level function. It's a noticeable cost to have these inlined w/o good reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated, sounds good. I'll move all static functions outside of Component going forward.
function createEndpoint(typeConfig, baseEndpoint, props) { | ||
const {params, bindings} = props; | ||
const combinedParams = dict(); | ||
Object.assign(combinedParams, typeConfig['defaultParams'], params); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use const combinedParams = ({...typeConfig['defaultParams'], params})
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated!
/** | ||
* @private | ||
*/ | ||
function handleActivation() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto: move to top level and just pass finalEndpoint
as an arg.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated!
// Throws Warning for booleans, strings, 0, negative numbers | ||
// No errors when positive integer or equivalent string | ||
let checkedWidth = | ||
width === null || width === undefined ? DEFAULT_WIDTH : width; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x === null || x === undefined
is x == null
. But also we should be able to use ??
now. E.g. this expression can be done as width ?? DEFAULT_WIDTH
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to ??
and removed the if statement underneath.
width === null || width === undefined ? DEFAULT_WIDTH : width; | ||
let checkedHeight = | ||
height === null || height === undefined ? DEFAULT_HEIGHT : height; | ||
if (typeof checkedWidth === 'boolean' || !(Math.floor(checkedWidth) > 0)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this if
, tbh. Why would we expect the width to be a boolean vs, say, a function or an object? What would math.floor()
do if the width was indeed a string? IMHO, this whole warning we can do without.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, the reason I have this check for boolean is because Math.floor(true) actually returns 1, which would then not trigger the warning. Math.floor on a string converts the string to a number if it is a valid number or returns NaN which would trigger the warning.
So in combination with the above, null and undefined would silently be reassigned to be DEFAULT. Strings, objects, booleans, functions would trigger the warning. Valid positive strings and numbers will pass through unchanged and not trigger the warning.
The reasoning for including this was to be thorough so that in the case where the user accidentally passes an invalid width (or height), the component would resize to default and the user may not understand why.
Sounds like you think this is too much detail to check for. I am fine with removing the whole thing if you think so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, unless the logic differs by type, we shouldn't check types. Eventually we'll have to look into PropTypes
or d.ts
to define the prop types. Additionally, fractional values are fine as well, so we don't need to do Math.floor()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, I have updated this. There is no type checking now. It will only do the null coalescing operation (??
). We can update the type checking in the future when we get PropTypes
const type = select('type', knobConfigurations, knobConfigurations[0]); | ||
const href = text('shareEndpoint', 'Not Specified'); | ||
const params = object('params', {'subject': 'test'}); | ||
const bindings = object('bindings', { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, ok to defer to a follow up PR, but this prop bugs me the most. Putting on a user's hat on, I really don't understand why there are two params
and bindings
, and what's the difference between them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, I have created a comment in the parent issue to track outstanding items.
Thanks for the very thorough review @dvoytenko . I have noted that we will re-visit the way |
if (q < 0) { | ||
return ''; | ||
} else if (h < q) { | ||
return endpoint.substr(q); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't account for foo.html#this?is-a-hash-string
. Query must come before a hash. We could normalize if (h === -1) { h = endpoint.length }
to solve this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Justin, I've updated this function with your recommendation. Are there any other use cases you can think of? Or a util that I'm not aware of that already does this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I mentioned it elsewhere, but url.js's parseUrl().queryString
should be able to do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point on parseUrlDeprecated
deprecation. @jridgewell do you know what we deprecated it with? Is there something else we should be using now?
} | ||
if (bindings) { | ||
Object.keys(bindings).forEach((name) => { | ||
combinedBindings[name.toUpperCase()] = bindings[name] || ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we intended for any binding to be used here, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you elaborate on your question? In this section, the bindings object coming from props is being added into the combinedBindings. Then in the next part we replace variables in finalEndpoint with bindings from combinedBindings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can pass in bindings: { foobar: 'x' }
, where foobar
is some random binding name, and not one of the typeConfig['bindings']
. I think this is part of the confusion with having params
and binding
, we're allowing generic find-and-replace to happen where we meant to just allow you to replace a few strings in our static config JSON with something else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see your concern. The API from the original component behaves as you described.
In the previous version of the component, the binding find and replace happens asynchronously (in urlReplacements.expandUrlAsync()
) and was limited to only those bindings included in our config.
Now the logic is split into two parts, the (synchronous) find and replace logic lives inside the preact component and we will would have to do an asynchronous call within the amp component (since this needs to be within the amp environment) to get our binding values. Then pass the bindings into our preact component.
As a side effect to this, someone using the preact component in isolation would be able to add additional generic bindings like you described (bindings: {foobar: 'x' }
. I don't see this as a problem though since the user actually gets more flexibility with how they want to use the component and could potentially add other variables specific to the preact environment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the model we should try to get to is that the component only has a notion of props/params. And the whole async binding->params mapping is done wholly on the AMP side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, let's work toward this model in the AMP component design!
@@ -194,7 +194,7 @@ function getQueryString(endpoint) { | |||
let h = endpoint.indexOf('#'); | |||
q = q === -1 ? endpoint.length : q; | |||
h = h === -1 ? endpoint.length : h; | |||
return h < q ? '' : endpoint.substr(q, h - q); | |||
return endpoint.substr(q, h - q); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: We can use slice
since it uses endIndex
param instead of length
:
return endpoint.substr(q, h - q); | |
return endpoint.slice(q, h); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this is much cleaner, updated!
} | ||
if (bindings) { | ||
Object.keys(bindings).forEach((name) => { | ||
combinedBindings[name.toUpperCase()] = bindings[name] || ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can pass in bindings: { foobar: 'x' }
, where foobar
is some random binding name, and not one of the typeConfig['bindings']
. I think this is part of the confusion with having params
and binding
, we're allowing generic find-and-replace to happen where we meant to just allow you to replace a few strings in our static config JSON with something else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with two comments. The next step here should definitely be unwinding the confusion between bindings and params. It's certainly ok to do a detour to AMP part first, but we'll likely bump into the same issue there right away.
/** | ||
* @param {!JsonObject} props | ||
* @param {?string} name | ||
* @return {struct} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will look like this:
@return {{
typeConfig: !JsonObject,
baseEndpoint: string,
checkedWidth: number,
checkedHeight: number,
}}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I have updated the struct definition!
* {number} checkedWidth | ||
* {number} checkedHeight | ||
*/ | ||
function checkProps(props, name) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can remove name
from args and just use NAME
everywhere instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, updated!
Partial for #28283
Moved logic to configure the share endpoint with search params into preact component. This does not include changes needed to amp component.
Open questions
4/30 --
Latest commit updates the Number.isInteger to use Math.floor()
Adds in a storybook for this component
5/11 --
Renamed from 0.2 to 1.0 for directory name