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

#7670: Simplify creation of isolated widgets (IsolatedComponent) #8151

Merged
merged 26 commits into from
Apr 10, 2024

Conversation

fregante
Copy link
Collaborator

@fregante fregante commented Apr 4, 2024

What does this PR do?

You can find the implementation details in the POC PR above.

Future work

Checklist

  • Add tests and/or storybook stories
  • Designate a primary reviewer: @grahamlangford

"\\.ya?ml$": "yaml-jest-transform",
"\\.txt$": "<rootDir>/src/testUtils/rawJestTransformer.mjs",
// Note: `?param` URLs aren't supported here: https://github.com/jestjs/jest/pull/6282
// You can only use a mock via `moduleNameMapper` for these.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I updated these to avoid confusion. See comment/link.

@@ -15,25 +15,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import "primereact/resources/themes/saga-blue/theme.css";
import "primereact/resources/primereact.min.css";
import "primeicons/primeicons.css";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We can just load the stylesheets normally now 🎉

const ShadowRoot = EmotionShadowRoot.div!;
// TODO: Use EmotionShadowRoot["pixiebrix-widget"] to avoid any CSS conflicts. Requires snapshot/test updates
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I tried this in the POC PR, but it affected too many tests. I can try again later.

// Don't inherit the selection color
:host::selection {
background: initial;
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

A full default stylesheet in a documented CSS file 🎉


// This cannot be a CSS module or URL because it must live inside the
// shadow DOM and synchronously set the :host element style.
import cssText from "./IsolatedComponent.scss?loadAsText";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is the reason why I changed the Jest config

}

return false;
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You can find the context in webpack-contrib/mini-css-extract-plugin#1092. In short, webpack injects the stylesheet in the main document, so we need to target and remove it. This is also mentioned in the code below.

whichever website it's rendering on.
To support react-select and any future potential emotion components we used the
emotion variant of the react-shadow library.
*/}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Moved to EmotionShadowRoot

font-weight: 400;
line-height: 1.5;
color: #212529;
letter-spacing: normal;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nothing lasts longer than quick temporary fixes:

This was resolved by:

I dropped it here because the file is now an actual CSS module so the classes had to change.

Copy link

codecov bot commented Apr 6, 2024

Codecov Report

Attention: Patch coverage is 37.14286% with 44 lines in your changes are missing coverage. Please review.

Project coverage is 73.38%. Comparing base (c295c6c) to head (efb53fe).
Report is 1 commits behind head on main.

❗ Current head efb53fe differs from pull request most recent head 1334a91. Consider uploading reports for the commit 1334a91 to get more accurate results

Files Patch % Lines
src/components/IsolatedComponent.tsx 22.22% 21 Missing ⚠️
...ents/selectionToolPopover/SelectionToolPopover.tsx 25.00% 9 Missing ⚠️
src/components/Stylesheets.tsx 52.94% 8 Missing ⚠️
.../selectionToolPopover/showSelectionToolPopover.tsx 50.00% 4 Missing ⚠️
src/bricks/renderers/propertyTable.tsx 50.00% 1 Missing ⚠️
...ript/textSelectionMenu/selectionMenuController.tsx 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8151      +/-   ##
==========================================
- Coverage   73.43%   73.38%   -0.05%     
==========================================
  Files        1317     1315       -2     
  Lines       40780    40762      -18     
  Branches     7570     7579       +9     
==========================================
- Hits        29947    29915      -32     
- Misses      10833    10847      +14     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

style={
{
"fontSize": "16px",
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🆕 The storybook now works, however the component includes absolute positioning so it's cut. It can be adjusted separately.

Comment on lines +121 to +130
<IsolatedComponent
name="PropertyTree"
lazy={async () =>
import(
/* webpackChunkName: "isolated/PropertyTree" */
"./PropertyTree"
)
}
factory={(PropertyTree) => <PropertyTree value={value} />}
/>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is the format I ended up with, I think it's compact and readable enough, which is important due to all the restrictions:

  1. name, isolated/${name}, "/${name}" must all match. Unfortunately this cannot use a variable since they're used by webpack. I can add a lint rule though.
  2. the imported package must also appear in the isolatedComponentList.mjs file, which is picked up by webpack, by <IsolatedComponent/> itself for verification, and by ESLint eventually
  3. the lazy/factory attribute names are the best I could come up with. suggestions welcome (one is React.lazy(LAZY) and the other one returns the result of COMPONENT in const COMPONENT = React.lazy(LAZY)

Copy link
Collaborator

@mnholtz mnholtz Apr 8, 2024

Choose a reason for hiding this comment

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

I'd like to document some of these requirements all in one place somewhere (e.g. it's not clear just looking at IsolatedComponent.tsx that you'd need to add that component to isolatedComponentList). Either in the docstring directly or in a README that's referenced in the docstring.

If I'm looking to add a new isolated component, what are the steps that need to be taken and out-of-the box behaviors or assumptions that are made?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes definitely it needs some documentation and a lint rule, but:

  • the JSDoc includes a full example that is identical every time, where only the name and import path need to be changed, usually
  • if it's used incorrectly, either webpack, typescript or the component itself will throw, depending on what part is wrong

I can't think of anything that breaks that isn't already caught but the build.

assumptions that are made

Since "PropertyTree" is now loaded as an isolated component, it needs to import all the stylesheets it needs, including bootstrap if it needs it (it doesn't in this case). No component should need to use <Stylesheets/> directly unless it's loading the user's stylesheets.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'd like to document some of these requirements all in one place somewhere

I just checked, each prop is already documented and it includes the requirement:

/**
* It must match the `import()`ed component's filename
*/
name: string;
/**
* It must follow the format `isolated/${name}` specified above
* @example () => import(/* webpackChunkName: "isolated/Moon" * /, "@/components/Moon")
*/
lazy: LazyFactory<T>;

Also this:

if (!isolatedComponentList.some((url) => url.endsWith("/" + name))) {
throw new Error(
`Isolated component "${name}" is not listed in isolatedComponentList.mjs. Add it there and restart webpack to create it.`,
);
}

@fregante fregante changed the title WIP: #7670: Simplify creation of isolated widgets (IsolatedWidget) #7670: Simplify creation of isolated widgets (IsolatedWidget) Apr 6, 2024
"\\.(gif|svg|png)$": "<rootDir>/src/__mocks__/stringMock.js",

"\\?loadAsUrl$": "<rootDir>/src/__mocks__/stringMock.js",
"\\?loadAsText$": "<rootDir>/src/__mocks__/stringMock.js",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this conflict with the loadAsText one on line 109 below?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Given that tests and snapshots don't need to be updated, I'm guessing that it's not actually being applied nor needed.

Anyway I think these 4 above should be moved below.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I just wondered if it's related to the changes to imports in IntegrationConfigEditorModal.test.tsx

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sort of. I dropped it from that test file because:

  • ?loadAsText did not work in that file, as the original comment suggested, so having it there did nothing
  • this new config made that import etc?loadAsText point to the stringMock file instead of the actual yml file

Since ?loadAsText cannot "load the import as text" in Jest, it cannot be used if your tests depend on the content of the file.

I cleaned up line 109 as well: 1334a91

Copy link

No loom links were found in the first post. Please add one there if you'd like to it to appear on Slack.

Do not edit this comment manually.

@fregante fregante merged commit 5a4565c into main Apr 10, 2024
28 of 29 checks passed
@fregante fregante deleted the F/feature/isolated-components branch April 10, 2024 12:51
@fregante fregante restored the F/feature/isolated-components branch April 10, 2024 15:16
@fregante fregante deleted the F/feature/isolated-components branch April 10, 2024 15:39
@grahamlangford grahamlangford added this to the 1.8.13 milestone Apr 22, 2024
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.

None yet

4 participants