@khanacademy/wonder-blocks-core
Advanced tools
Comparing version 4.3.0 to 4.3.1
# @khanacademy/wonder-blocks-core | ||
## 4.3.1 | ||
## 4.3.0 | ||
@@ -4,0 +6,0 @@ |
@@ -32,8 +32,6 @@ import _extends from '@babel/runtime/helpers/extends'; | ||
}; | ||
} // Check to see if we should inline all the styles for snapshot tests. | ||
} | ||
const shouldInlineStyles = typeof global !== "undefined" && global.SNAPSHOT_INLINE_APHRODITE; | ||
flatten(style).forEach(child => { | ||
// Check for aphrodite internal property | ||
const _definition = child._definition; | ||
@@ -43,10 +41,5 @@ | ||
if (shouldInlineStyles) { | ||
const def = {}; // React 16 complains about invalid keys in inline styles. | ||
// It doesn't accept kebab-case in media queries and instead | ||
// prefers camelCase. | ||
const def = {}; | ||
for (const [key, value] of Object.entries(_definition)) { | ||
// This regex converts all instances of -{lowercaseLetter} | ||
// to the uppercase version of that letter, without the | ||
// leading dash. | ||
def[key.replace(/-[a-z]/g, match => match[1].toUpperCase())] = value; | ||
@@ -63,7 +56,3 @@ } | ||
}); | ||
const inlineStylesObject = Object.assign.apply(Object, [{}].concat(inlineStyles)); // TODO(somewhatabstract): When aphrodite no longer puts "!important" on | ||
// all the styles, remove this <ADD JIRA ISSUE HERE IF THIS PASSES REVIEW> | ||
// If we're not snapshotting styles, let's create a class for the inline | ||
// styles so that they can apply to the element even with aphrodite's | ||
// use of !important. | ||
const inlineStylesObject = Object.assign.apply(Object, [{}].concat(inlineStyles)); | ||
@@ -87,5 +76,2 @@ if (inlineStyles.length > 0 && !shouldInlineStyles) { | ||
text: { | ||
// Disable subpixel antialiasing on Mac desktop for consistency of | ||
// rendering with mobile and Sketch (neither of which support it). | ||
// See https://bjango.com/articles/subpixeltext/ for more details. | ||
WebkitFontSmoothing: "antialiased", | ||
@@ -95,4 +81,2 @@ MozOsxFontSmoothing: "grayscale" | ||
header: { | ||
// User agent stylesheets add vertical margins to header tags by | ||
// default. We prefer to be more deliberate in our spacing instead. | ||
marginTop: 0, | ||
@@ -102,15 +86,2 @@ marginBottom: 0 | ||
}); | ||
/** | ||
* Text is a building block for constructing other components. `Text` roughly | ||
* maps to `span`. You can override which tag is used to render the component | ||
* (for semantic purposes) by specifying the `tag` prop. | ||
* | ||
* These components can take styles (via the `style` prop) in a variety of | ||
* manners: | ||
* | ||
* - An inline style object | ||
* - An `aphrodite` StyleSheet style | ||
* - An array combining the above | ||
*/ | ||
class Text extends React.Component { | ||
@@ -129,3 +100,3 @@ render() { | ||
const styleAttributes = processStyleList([styles$1.text, isHeader && styles$1.header, style]); | ||
return /*#__PURE__*/React.createElement(Tag, _extends({}, otherProps, { | ||
return React.createElement(Tag, _extends({}, otherProps, { | ||
style: styleAttributes.style, | ||
@@ -143,3 +114,2 @@ className: styleAttributes.className, | ||
const _excluded$1 = ["className", "style"]; | ||
// TODO(kevinb): have an a version which uses exact object types | ||
function addStyle(Component, defaultStyle) { | ||
@@ -151,6 +121,4 @@ function StyleComponent(props) { | ||
} = props, | ||
tmpOtherProps = _objectWithoutPropertiesLoose(props, _excluded$1); // NOTE(jeresig): We need to cast the remaining props to be the right | ||
// value to ensure that they're typed properly. | ||
tmpOtherProps = _objectWithoutPropertiesLoose(props, _excluded$1); | ||
const otherProps = tmpOtherProps; | ||
@@ -162,3 +130,3 @@ const reset = typeof Component === "string" ? overrides[Component] : null; | ||
} = processStyleList([reset, defaultStyle, style]); | ||
return /*#__PURE__*/React.createElement(Component, _extends({}, otherProps, { | ||
return React.createElement(Component, _extends({}, otherProps, { | ||
className: [aphroditeClassName, className].filter(Boolean).join(" "), | ||
@@ -171,15 +139,7 @@ style: inlineStyles | ||
} | ||
/** | ||
* These are necessary to override various custom styles that browsers add so that | ||
* elements have consistent styles across all browsers. Only add styles here if | ||
* they appear in https://github.com/necolas/normalize.css/blob/master/normalize.css. | ||
*/ | ||
const overrides = StyleSheet.create({ | ||
button: { | ||
margin: 0, | ||
// Safari adds 2px left/right margins | ||
"::-moz-focus-inner": { | ||
border: 0 // Firefox adds an inner focus ring around text | ||
border: 0 | ||
} | ||
@@ -191,3 +151,2 @@ } | ||
const styles = StyleSheet.create({ | ||
// https://github.com/facebook/css-layout#default-values | ||
default: { | ||
@@ -204,3 +163,2 @@ alignItems: "stretch", | ||
zIndex: 0, | ||
// fix flexbox bugs | ||
minHeight: 0, | ||
@@ -215,16 +173,2 @@ minWidth: 0 | ||
const StyledSection = addStyle("section", styles.default); | ||
/** | ||
* View is a building block for constructing other components. `View` roughly | ||
* maps to `div` and `Text` roughly maps to `span`. You can override which tag | ||
* is used to render the component (for semantic purposes) by specifying the | ||
* `tag` prop. | ||
* | ||
* These components can take styles (via the `style` prop) in a variety of | ||
* manners: | ||
* | ||
* - An inline style object | ||
* - An `aphrodite` StyleSheet style | ||
* - An array combining the above | ||
*/ | ||
class View extends React.Component { | ||
@@ -245,15 +189,15 @@ render() { | ||
case "article": | ||
return /*#__PURE__*/React.createElement(StyledArticle, props); | ||
return React.createElement(StyledArticle, props); | ||
case "aside": | ||
return /*#__PURE__*/React.createElement(StyledAside, props); | ||
return React.createElement(StyledAside, props); | ||
case "nav": | ||
return /*#__PURE__*/React.createElement(StyledNav, props); | ||
return React.createElement(StyledNav, props); | ||
case "section": | ||
return /*#__PURE__*/React.createElement(StyledSection, props); | ||
return React.createElement(StyledSection, props); | ||
case "div": | ||
return /*#__PURE__*/React.createElement(StyledDiv, props); | ||
return React.createElement(StyledDiv, props); | ||
@@ -275,49 +219,4 @@ default: | ||
}); | ||
/** | ||
* This is the context that tracks who is doing what in our SSR component tree. | ||
* | ||
* root: | ||
* no one has instigated an initial SSR render so the component that sees | ||
* this "root" state is responsible for controlling initial versus standard | ||
* rendering semantics | ||
* | ||
* initial: | ||
* this means the SSR render has started, and all SSR components should act | ||
* as though they are on the server | ||
* | ||
* standard: | ||
* means that we're all now doing non-SSR rendering | ||
*/ | ||
const RenderStateContext = React.createContext(RenderState.Root); | ||
const RenderStateContext = /*#__PURE__*/React.createContext(RenderState.Root); | ||
/** | ||
* We use render functions so that we don't do any work unless we need to. | ||
* This avoids rendering but not mounting potentially complex component trees. | ||
*/ | ||
/** | ||
* Defer or change rendering until the component did mount. | ||
* | ||
* The purpose of this component is to disable or modify serverside rendering | ||
* of certain components. Disabling rendering on the server, by itself, would | ||
* not be sufficient, since the initial render of the component must match | ||
* what is rendered on the server. Therefore, this component also disables | ||
* rendering the first time around on the client. | ||
* | ||
* If `WithSSRPlaceholder` components are nested within one another, | ||
* the root `WithSSRPlaceholder` component will handle the initial | ||
* render, but nested `WithSSRPlaceholder` components will delegate to | ||
* the root one, meaning that we don't cascade delayed rendering down | ||
* the component tree. This will also be the case across portal | ||
* boundaries. | ||
* | ||
* Example: | ||
* | ||
* ```js | ||
* <WithSSRPlaceholder placeholder={() => <div>Renders on the server!</div>}> | ||
* {() => <div>Only renders on the client (after rehydration).</div>} | ||
* </WithSSRPlaceholder> | ||
* ``` | ||
*/ | ||
class WithSSRPlaceholder extends React.Component { | ||
@@ -334,5 +233,2 @@ constructor(...args) { | ||
if (this._isTheRootComponent) { | ||
// We only want to force a new render if we were responsible for | ||
// the first render, so we guard that state change here. | ||
// eslint-disable-next-line react/no-did-mount-set-state | ||
this.setState({ | ||
@@ -351,27 +247,17 @@ mounted: true | ||
placeholder | ||
} = this.props; // We are the first component in the tree. | ||
// We are in control of instigating a second render for our | ||
// component tree. | ||
} = this.props; | ||
this._isTheRootComponent = true; | ||
if (mounted) { | ||
// This is our second non-SSR render, so let's tell everyone to | ||
// do their thing. | ||
return /*#__PURE__*/React.createElement(RenderStateContext.Provider, { | ||
return React.createElement(RenderStateContext.Provider, { | ||
value: RenderState.Standard | ||
}, children()); | ||
} // OK, this is the very first render. | ||
// If we have a placeholder, we render it, and ensure that any | ||
// nested SSR components know we're still on that first render | ||
// but they're not in charge of instigating the second render. | ||
} | ||
if (placeholder) { | ||
return /*#__PURE__*/React.createElement(RenderStateContext.Provider, { | ||
return React.createElement(RenderStateContext.Provider, { | ||
value: RenderState.Initial | ||
}, placeholder()); | ||
} // Otherwise, we return nothing. | ||
} | ||
return null; | ||
@@ -391,42 +277,14 @@ } | ||
case RenderState.Initial: | ||
// We're not the root component, so we just have to either | ||
// render our placeholder or nothing. | ||
// The second render is going to be triggered for us. | ||
if (placeholder) { | ||
return placeholder(); | ||
} // Otherwise, we render nothing. | ||
} | ||
return null; | ||
case RenderState.Standard: | ||
// We have covered the SSR render, we're now rendering with | ||
// standard rendering semantics. | ||
return children(); | ||
} // There are edge cases where for some reason, we get an unknown | ||
// context value here. So far it seems to be when we're nested in a | ||
// v1 WithSSRPlaceholder equivalent component, or in some older | ||
// React v16 situations where we're nested in the provider of a | ||
// different context. | ||
// | ||
// We ignore this from coverage. It's a maintenance case to help | ||
// us catch code changes that affect the control flow unexpectedly, | ||
// but it's not something we need to write a test case for. | ||
// | ||
// Flow will assert exhaustiveness of the switch because Flow enums | ||
// rock. | ||
// | ||
} | ||
/* istanbul ignore next */ | ||
{ | ||
// Let's log this case so we can debug it easily. | ||
// Then fall through to the root case. | ||
/* eslint-disable-next-line no-console */ | ||
console.log(`We got a render state we don't understand: "${JSON.stringify(renderState)}"`); // We "fallthrough" to the root case. This is more obvious | ||
// and maintainable code than just ignoring the no-fallthrough | ||
// lint rule. | ||
console.log(`We got a render state we don't understand: "${JSON.stringify(renderState)}"`); | ||
return this._maybeRender(RenderState.Root); | ||
@@ -437,3 +295,3 @@ } | ||
render() { | ||
return /*#__PURE__*/React.createElement(RenderStateContext.Consumer, null, value => this._maybeRender(value)); | ||
return React.createElement(RenderStateContext.Consumer, null, value => this._maybeRender(value)); | ||
} | ||
@@ -443,17 +301,3 @@ | ||
/** | ||
* This is NOT for direct use. Instead, see the UniqueIDProvider component. | ||
* | ||
* Implements IIdentifierFactory to provide unique identifiers. | ||
*/ | ||
class UniqueIDFactory { | ||
/** | ||
* Creates a UniqueIDFactory instance. | ||
* | ||
* @param {string} scope An optional case-insensitive scope for the | ||
* factory. This will be used as part of the identifier. Useful for | ||
* providing context to the identifiers, which can be useful in | ||
* differentiating elements when debugging the DOM. This must contain only | ||
* hyphen and alphanumeric characters. | ||
*/ | ||
constructor(scope) { | ||
@@ -479,10 +323,3 @@ this.get = key => { | ||
} | ||
/** | ||
* This method verifies that a string contains valid characters for an | ||
* identifier. It does not assert that a string IS a valid identifier (for | ||
* example, that it doesn't start with numbers). We don't need to do that | ||
* here because all identifiers are prefixed to avoid needing that check. | ||
*/ | ||
_hasValidIdChars(value) { | ||
@@ -496,26 +333,6 @@ if (typeof value !== "string") { | ||
} | ||
/** | ||
* Provides a unique identifier with the given key. | ||
* | ||
* @param {string} key The case-insensitive key of the identifier. | ||
* | ||
* @returns {string} A unique identifier that will remain the same for this | ||
* key in this factory. This must contain only hyphen and alphanumeric | ||
* characters. | ||
*/ | ||
} | ||
UniqueIDFactory._factoryUniquenessCounter = 0; | ||
/** | ||
* This is NOT for direct use. Instead, see the UniqueIDProvider component. | ||
* | ||
* Implements a version of IIdentifierFactory that can be used for providing | ||
* identifiers on initial render of components that are eligible for server-side | ||
* rendering. | ||
* | ||
* The identifiers are not guaranteed to be unique, but they will match between | ||
* server and the first client render. | ||
*/ | ||
class SsrIDFactory { | ||
@@ -531,18 +348,2 @@ get(id) { | ||
/** | ||
* The `UniqueIDProvider` component is how Wonder Blocks components obtain | ||
* unique identifiers. This component ensures that server-side rendering and | ||
* initial client rendering match while allowing the provision of unique | ||
* identifiers for the client. | ||
* | ||
* In all but the first render, the children are rendered with the same | ||
* `IIdentifierFactory` instance, ensuring that the same calls will return the | ||
* same identifiers. | ||
* | ||
* The `get` method of the identifier factory ensures that the same identifier | ||
* is returned for like requests, but also that all identifiers provided are | ||
* unique. Therefore, `get("test")` will always equal `get("test")`, and | ||
* `get("test2")` will always equal `get("test2")`, but `get("test")` will | ||
* never equal `get("test2")`. | ||
*/ | ||
class UniqueIDProvider extends React.Component { | ||
@@ -554,10 +355,6 @@ _performRender(firstRender) { | ||
scope | ||
} = this.props; // If this is our first render, we're going to stop right here. | ||
// Note: `firstRender` will be `false` on the first render if this | ||
// component is a descendant of a `WithSSRPlaceholder`. | ||
} = this.props; | ||
if (firstRender) { | ||
if (mockOnFirstRender) { | ||
// We're allowing an initial render, so let's pass our mock | ||
// identifier factory to support SSR. | ||
return children(SsrIDFactory$1); | ||
@@ -567,10 +364,8 @@ } | ||
return null; | ||
} // Create an identifier factory if we don't already have one | ||
} | ||
if (!this._idFactory) { | ||
this._idFactory = new UniqueIDFactory(scope); | ||
} // It's a regular render, so let's use our identifier factory. | ||
} | ||
return children(this._idFactory); | ||
@@ -580,6 +375,3 @@ } | ||
render() { | ||
// Here we use the WithSSRPlaceholder component to control | ||
// when we render and whether we provide a mock or real | ||
// identifier factory. | ||
return /*#__PURE__*/React.createElement(WithSSRPlaceholder, { | ||
return React.createElement(WithSSRPlaceholder, { | ||
placeholder: () => this._performRender(true) | ||
@@ -591,16 +383,2 @@ }, () => this._performRender(false)); | ||
/** | ||
* This is a wrapper that returns an identifier. If the `id` prop is set, the component will | ||
* return the same id to be consumed by its children. Otherwise, a unique id will be provided. | ||
* This is beneficial for accessibility purposes, among other things. | ||
* | ||
* The main difference with UniqueIDProvider is that IDProvider has a single responsibility, | ||
* to return an identifier that can by used by the children that are rendered internally. | ||
* | ||
* This way, the wrapped component will receive this custom ID and will use it to connect | ||
* different elements. | ||
* | ||
* e.g. It uses the same generated id to connect a Dialog with its main title, or form label | ||
* with the associated input element, etc. | ||
*/ | ||
class IDProvider extends React.Component { | ||
@@ -628,7 +406,5 @@ renderChildren(ids) { | ||
if (id) { | ||
// Let's bypass the extra weight of an id provider since we don't | ||
// need it. | ||
return this.renderChildren(); | ||
} else { | ||
return /*#__PURE__*/React.createElement(UniqueIDProvider, { | ||
return React.createElement(UniqueIDProvider, { | ||
scope: scope, | ||
@@ -645,13 +421,3 @@ mockOnFirstRender: true | ||
var server = { | ||
/** | ||
* Check if we are running in server-side mode. | ||
* | ||
* @returns {boolean} `true` if we are in server-side mode; otherwise, | ||
* `false` | ||
*/ | ||
isServerSide: () => serverSide, | ||
/** | ||
* Set server-side mode to true. | ||
*/ | ||
setServerSide: () => { | ||
@@ -662,10 +428,2 @@ serverSide = true; | ||
/** | ||
* Returns a unique identifier factory. If the parent component hasn't | ||
* been mounted yet, the global SsrIDFactory will be returned until the | ||
* component becomes mounted. | ||
* | ||
* @param {string} [scope] optional string to prefix generated ids with. | ||
* @returns {IIdentifierFactory} | ||
*/ | ||
const useUniqueIdWithMock = scope => { | ||
@@ -689,10 +447,2 @@ const renderState = useContext$1(RenderStateContext); | ||
}; | ||
/** | ||
* Returns a unique identifier factory. If the parent component hasn't | ||
* been mounted yet, null will be returned. | ||
* | ||
* @param {string} [scope] optional string to prefix generated ids with. | ||
* @returns {?IIdentifierFactory} | ||
*/ | ||
const useUniqueIdWithoutMock = scope => { | ||
@@ -717,15 +467,2 @@ const renderState = useContext$1(RenderStateContext); | ||
/** | ||
* Hook for forcing a component to update on demand. | ||
* | ||
* This is for use inside other hooks that do some advanced | ||
* trickery with storing state outside of React's own state | ||
* mechanisms. As such this should never be called directly | ||
* outside of a hook, and more often than not, is the wrong | ||
* choice for whatever you are trying to do. If in doubt, | ||
* don't use it. | ||
* | ||
* @returns {() => void} A function that forces the component to update. | ||
*/ | ||
const useForceUpdate = () => { | ||
@@ -750,3 +487,3 @@ const [, setState] = React.useState(false); | ||
setFirstRender(false); | ||
}, []); // This effect will only run once. | ||
}, []); | ||
@@ -756,6 +493,4 @@ if (contextValue !== RenderState.Root) { | ||
throw new Error("There's already a <RenderStateRoot> above this instance in " + "the render tree. This instance should be removed."); | ||
} // Avoid rendering multiple providers if this RenderStateRoot | ||
// is nested inside another one. | ||
} | ||
return children; | ||
@@ -765,3 +500,3 @@ } | ||
const value = firstRender ? RenderState.Initial : RenderState.Standard; | ||
return /*#__PURE__*/React.createElement(RenderStateContext.Provider, { | ||
return React.createElement(RenderStateContext.Provider, { | ||
value: value | ||
@@ -768,0 +503,0 @@ }, children); |
{ | ||
"name": "@khanacademy/wonder-blocks-core", | ||
"version": "4.3.0", | ||
"version": "4.3.1", | ||
"design": "v1", | ||
@@ -27,3 +27,3 @@ "publishConfig": { | ||
"devDependencies": { | ||
"wb-dev-build-settings": "^0.3.0" | ||
"wb-dev-build-settings": "^0.4.0" | ||
}, | ||
@@ -30,0 +30,0 @@ "author": "", |
@@ -48,3 +48,3 @@ Adds a `style` property to a component. A component can be a Wonder Blocks | ||
[See source file](https://github.com/Khan/wonder-blocks/blob/master/flow-typed/aphrodite.flow.js#L13) | ||
[See source file](https://github.com/Khan/wonder-blocks/blob/main/flow-typed/aphrodite.flow.js#L13) | ||
@@ -51,0 +51,0 @@ |
227353
4282