-
Notifications
You must be signed in to change notification settings - Fork 184
feat: Widgetize app layout skeleton event base #3497
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
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #3497 +/- ##
==========================================
- Coverage 96.96% 96.90% -0.07%
==========================================
Files 820 828 +8
Lines 23786 23999 +213
Branches 8337 8432 +95
==========================================
+ Hits 23064 23256 +192
+ Misses 716 692 -24
- Partials 6 51 +45 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
ReactDOM.render(<AppComponent />, innerAppRoot); | ||
return () => { | ||
syncClassesCleanup(); | ||
ReactDOM.unmountComponentAtNode(innerAppRoot); |
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 cleanup does not work because it is inside a timeout now
@@ -154,6 +157,9 @@ describe('AppLayoutToolbar renders correct analytics metadata', () => { | |||
toolsClose: 'close tools', | |||
}, | |||
}); | |||
await waitFor(() => { | |||
expect(wrapper.findToolsToggle()).toBeTruthy(); | |||
}); | |||
const toolsTrigger = wrapper.findToolsToggle().getElement(); | |||
validateComponentNameAndLabels(toolsTrigger, {}); | |||
expect(getGeneratedAnalyticsMetadata(toolsTrigger)).toEqual({ |
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 it have any issues for analytics if these metadata is rendered too late
const { wrapper } = renderComponent(<AppLayout />); | ||
|
||
expect(wrapper.findNavigationToggle()).toBeTruthy(); | ||
expect(wrapper.findNavigation()).toBeTruthy(); | ||
expect(wrapper.findNavigationClose()).toBeTruthy(); | ||
expect(wrapper.findToolsToggle()).toBeTruthy(); | ||
await waitFor(() => { |
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.
If everything becomes async, this is a breaking change for our consumers.
All of their unit tests will fail, won't they?
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.
Explored deeper, there seems to be a solution. One simple change allowed to remove some of them: https://github.com/cloudscape-design/components/pull/3648/files?w=1#diff-3cfa742d60cd325bf84e2694ddf9a04bf3ebdda6cd27919a6dec23caac3953d0
@@ -26,8 +26,8 @@ jest.mock('../../../lib/components/internal/widgets', () => ({ | |||
createWidgetizedComponent: createWidgetizedComponentMock, | |||
})); | |||
|
|||
describeEachAppLayout({ themes: ['refresh-toolbar'] }, () => { | |||
it('renders complete component by default', () => { | |||
describeEachAppLayout({ themes: ['refresh-toolbar'], skipInitialTest: true }, () => { |
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 happens with initial test in this case?
Instead of skipping the test, can we rewrite this test so it only asserts synchronously available parts?
@@ -65,7 +66,6 @@ describeEachAppLayout({ themes: ['refresh-toolbar'] }, () => { | |||
expect(wrapper.findToolbar()).toBeFalsy(); | |||
expect(wrapper.findNavigation()).toBeFalsy(); | |||
expect(wrapper.findBreadcrumbs()).toBeFalsy(); | |||
expect(wrapper.find(getFunnelKeySelector('funnel-name'))).toBeTruthy(); |
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 removed? This is one of critical feature
@@ -83,7 +91,10 @@ export function useMultipleFocusControl( | |||
const shouldFocus = useRef(false); | |||
|
|||
useEffect(() => { | |||
doFocus(activeDrawersIds[0]); | |||
const drawerId = activeDrawersIds[0]; | |||
refs.current[drawerId]?.focusPromise?.promise?.then(() => { |
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.
If we introduced a separate useAsyncFocusControl
, do we really need to change this one?
}; | ||
|
||
useEffect(() => { | ||
focusPromise.current.promise.then(() => { |
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.
Should we cancel this waiting if the state changes before the promise is resolved?
useEffect(() => { | ||
const timeoutId = setTimeout(() => { | ||
setMount(true); | ||
}, 1000); | ||
|
||
return () => clearTimeout(timeoutId); | ||
}, []); |
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 code runs is production, even if enableDelayedComponents = false
To avoid this, the check should happen right under createAppLayoutPart
export function createAppLayoutPart({ Component }) {
if(!enableDelayedComponents) {
return Component;
}
// delayed mock
}
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.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.
Double copyright header
const enableDelayedComponents = false; | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export function createAppLayoutPart({ Component }: { Component: React.JSXElementConstructor<any> }) { |
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.
Should we reuse it from the other place?
// type casting is safe here since 'drawers' only exists at runtime after appLayoutInternals | ||
// has been initialized | ||
<AppLayoutDrawer appLayoutInternals={appLayoutInternals as AppLayoutInternals} /> |
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.
Sometimes you do appLayoutInternals as AppLayoutInternals
and sometimes appLayoutInternals!
, why it is different?
Can we have a unified way to check "is app layout widget loaded", so all code down the line knows that accessing values is safe
sharedStyles['with-motion-horizontal'], | ||
resolvedNavigationOpen && !toolsOpen && styles['unfocusable-mobile'], | ||
toolsOpen && styles['tools-open'], | ||
drawerExpandedMode && styles.hidden |
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.
Wasn't the original plan to abstract away all features and use only generic names in skeleton code?
So this is not the drawerExpandedMode
but isPanelHidden
, which joins all !toolsOpen
, drawerExpandedMode
and everything else that may require hiding the panel?
</div> | ||
<div className={clsx(styles['global-tools'], !globalToolsOpen && styles['panel-hidden'])}> |
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.
Didn't we want to move this panel separation logic into the widget to make it more generic here?
const { splitPanelPosition, appLayoutInternals, splitPanelInternals } = appLayoutState ?? {}; | ||
return ( | ||
<> | ||
{splitPanelPosition === 'bottom' && ( |
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.
How can we insert a new bottom panel here without touching the skeleton if it has a hard-coded mention of the split panel?
const { wrapper } = renderComponent(<AppLayout />); | ||
|
||
expect(wrapper.findNavigationToggle()).toBeTruthy(); | ||
expect(wrapper.findNavigation()).toBeTruthy(); | ||
expect(wrapper.findNavigationClose()).toBeTruthy(); | ||
expect(wrapper.findToolsToggle()).toBeTruthy(); | ||
await waitFor(() => { |
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.
Explored deeper, there seems to be a solution. One simple change allowed to remove some of them: https://github.com/cloudscape-design/components/pull/3648/files?w=1#diff-3cfa742d60cd325bf84e2694ddf9a04bf3ebdda6cd27919a6dec23caac3953d0
f344749
to
9c84089
Compare
9c84089
to
98d26f7
Compare
7508689
to
51d4995
Compare
378f8a6
to
171d166
Compare
4cbcf55
to
ad8f1d7
Compare
497adbb
to
7f5aec4
Compare
7f5aec4
to
54668d4
Compare
54668d4
to
8520636
Compare
6ab60c6
to
e9a4d1b
Compare
chore: Move skeleton elements attributes to a widgetized hook chore: Move skeleton slots to widgets chore: Widgetize all remaining components feat: AppLayout state as widget chore: small fix chore: small fix chore: Small refactoring chore: Pull react-reverse-portal as src util chore: Revert unrelated changes in runtime-drawers.page.tsx fix: SSR tests fix: Skeleton part tests feat: Deliver skeleton slots attributes as a widget part fix: Unit tests chore: Update snapshots chore: Tests for reverse portal chore: Additional tests for reverse portal fix: Integ tests chore: Skip hash for class names in widget contract tests fix: Tests chore: Wrap up chore: Fallbacks for app layout component when is in loading state (avoiding layout cumulative shifting) chore: Delay breadcrumbs rendering until app layout state is loaded feat: Event-base prop passing Fix u tests and remove unused reverse-portal feat: Split app layout state into widgetized and built-in parts chore: Merge expanded mode chore: Merge from main fix: useMergeRefs import fix: remove unnecessary import fix: Navigation fallback for useMultiAppLayout chore: Refactoring, fixed tools test chore: Refactoring, fixed skeleton test chore: Update snapshots fix: header-variant test fix: toolbar test fix: multi-layout.test.tsx fix: slots.test.tsx fix: desktop.test.tsx chore: Update snapshots fix: split-panel.test.tsx fix: main.test.tsx fix: drawers.test.tsx fix: common.test.tsx fix: analytics-metadata.test.tsx chore: Update snapshots chore: Update snapshots feat: Async focus control chore: Migrate useUniqueId import to @cloudscape-design/component-toolkit fix: onMount type issue feat: Introduce useAsyncFocusControl chore: Clean up style hash in widget-contract-split-panel.test.tsx chore: Cleanup multi-layout-with-hidden-instances-iframe.page.tsx chore: Small test adjustment chore: Suppress an overlay for runtime errors in webpack config fix: Eslint error fix: AppLayoutPartLoader mounting issue chore: Increased a timeout for multi-layout.test.tsx chore: Types for a state manager chore: Wrapping up (type adjustments, snapshot upd) chore: Turn focusSplitPanel into async function (wait for an element before focusing on it) feat: Custom feature flag appLayoutDelayedWidget to test async widget behavior chore: Integ test for checking ensure the layout is not shifting after widget loading chore: Upd snapshot tests fix: Duplicate 'client' key in webpack.config.base.cjs fix: analytics metadata
e9a4d1b
to
fef642b
Compare
The work moved to #3721 |
Description
Related links, issue #, if available: n/a
How has this been tested?
Review checklist
The following items are to be evaluated by the author(s) and the reviewer(s).
Correctness
CONTRIBUTING.md
.CONTRIBUTING.md
.Security
checkSafeUrl
function.Testing
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.