-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
[AppProvider] Update context #1424
Conversation
|
||
interface State { | ||
polaris: Context; | ||
context: Context; |
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.
Changing this on the local state to be clearer about what it is
@@ -31,18 +29,9 @@ export interface AppProviderProps { | |||
} | |||
|
|||
export interface Context { | |||
polaris: { |
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.
Removing the polaris
namespace now since we consume it from a render prop rather than context types. We won't have to worry about collisions now
@@ -14,7 +14,7 @@ export interface StickyItem { | |||
/** Placeholder element */ | |||
placeHolderNode: HTMLElement; | |||
/** Element outlining the fixed position boundaries */ | |||
boundingElement: HTMLElement | null; | |||
boundingElement?: HTMLElement | null; |
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 was a type issue and boundElement
could be undefined
@@ -5,7 +5,7 @@ export { | |||
PrimitiveReplacementDictionary, | |||
ComplexReplacementDictionary, | |||
} from './Intl'; | |||
export {default as withSticky, StickyManager} from './withSticky'; |
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.
withSticky
was deleted
@@ -13,7 +14,11 @@ describe('withAppProvider', () => { | |||
const WrappedComponent = withAppProvider<any>()(Child); | |||
|
|||
const fn = () => { | |||
mount(<WrappedComponent />); | |||
mount( | |||
<AppProviderContext.Provider value={{} as 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.
Wrapping the component in a provider so withAppProvider
can consume it
appBridge: ClientApplication<{}>; | ||
subscribe(callback: () => void): void; | ||
unsubscribe(callback: () => void): void; | ||
theme?: ThemeContext; |
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 was a type issue and theme
/ appBridge
could always be undefined
? merge(WrappedComponent.contextTypes, polarisAppProviderContextTypes) | ||
: polarisAppProviderContextTypes; | ||
export interface Options { | ||
inScrollable?: boolean; |
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.
withScrollable
| inScrollable
? Any opinions?
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.
withinScrollable
?
src/components/AppProvider/utilities/withAppProvider/withAppProvider.tsx
Show resolved
Hide resolved
@@ -1,55 +0,0 @@ | |||
import * as React from 'react'; |
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 smaller more compact version was included in withAppProvider
so we can cut down on individual HoC
@@ -388,21 +388,6 @@ describe('<ComboBox/>', () => { | |||
}); | |||
|
|||
describe('keypress events', () => { | |||
it('selects the first option when the down arrow is pressed', () => { |
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 test doesn't do what's expected, the test below covers it.
@@ -430,7 +415,7 @@ describe('<ComboBox/>', () => { | |||
); | |||
|
|||
comboBox.simulate('focus'); | |||
expect(comboBox.state('popoverActive')).toBe(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.
We should never expect on state (component internals)
@@ -29,11 +29,4 @@ describe('<DisplayText />', () => { | |||
); | |||
expect(displayText.find('p')).toHaveLength(1); | |||
}); | |||
|
|||
it('renders the specified size', () => { |
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 shouldn't expect on the base components props since it doesn't provide value. This test should be handled with visual regression like percy
@@ -30,32 +30,6 @@ describe('<Layout />', () => { | |||
expect(section.find(MyComponent).exists()).toBe(true); | |||
}); | |||
|
|||
describe('<Layout.Section />', () => { |
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 shouldn't expect on the base components props since it doesn't provide value. These tests should be handled by percy
@@ -46,13 +46,16 @@ export class Page extends React.PureComponent<ComposedProps, never> { | |||
private titlebar: AppBridgeTitleBar.TitleBar | undefined; |
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.
Related to the correct appBridge type from context
@@ -36,7 +67,10 @@ describe('<PositionedOverlay />', () => { | |||
const positionedOverlay = mountWithAppProvider( | |||
<PositionedOverlay {...mockProps} preferredAlignment="left" />, | |||
); | |||
expect(positionedOverlay.prop('preferredAlignment')).toBe('left'); | |||
|
|||
expect( |
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 fixed this test and the test below but we should be relying on visual regression for these
@@ -30,8 +30,14 @@ export default class Sticky extends React.Component<Props, State> { | |||
private stickyNode: HTMLElement | null = null; | |||
|
|||
componentDidMount() { | |||
const {stickyManager} = this.context.polaris; | |||
const {boundingElement, offset, disableWhenStacked} = this.props; | |||
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.
The types were originally incorrect and weren't compatible with registerStickyItem
so I added defaults and an early return @dleroux
@@ -27,13 +24,7 @@ export function findByTestID(root: ReactWrapper<any, any>, id: string) { | |||
return wrapper.length > 0 && wrapper.prop('testID') === id; | |||
} | |||
|
|||
const rootResult = root.findWhere(hasTestID).first(); |
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 this to be compatible with our new testing util
@@ -82,98 +73,85 @@ export function trigger(wrapper: AnyWrapper, keypath: string, ...args: any[]) { | |||
return returnValue; | |||
} | |||
|
|||
/** |
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 a few functions below that are no longer in use
src/test-utilities/enzyme.tsx
Outdated
}) as any; | ||
interface MountWithAppProviderOptions { | ||
context?: { | ||
frame?: DeepPartial<FrameContext>; |
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.
DeepPartial
is used so we can only merge one branch of an object. For example, updating appBridge from undefined to X but leaving the rest of polaris intact.
): AppContextReactWrapper<P, any> { | ||
const {context: ctx = {}} = options; | ||
|
||
const polarisDefault = createPolarisContext(); |
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.
Creating a default and merging it with provided if it exists
// We need this wrapping div because Enzyme expects a single node, not an array. | ||
nodeJSX = <div>{nodeJSX}</div>; | ||
} | ||
const context: AppContext = { |
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.
Creating our context to pass too AppContextReactWrapper
src/test-utilities/enzyme.tsx
Outdated
options?: ShallowRendererProps, | ||
): ShallowWrapper<P, any> { | ||
return shallow(node, mergeAppProviderOptions(options)).dive(options); | ||
this.app = app; |
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 now access context, for example
const x = mountWithAppProvider(<Component />)
console.log(x.app.polaris.intl.translate(...)
// Unfortunately, this is how we have to type this at the moment. | ||
// There is currently a proposal to support variadic kinds. | ||
// https://github.com/Microsoft/TypeScript/issues/5453 | ||
function merge<TSource1, TSource2>( |
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 needed a stronger type for merge
tsconfig.json
Outdated
@@ -8,7 +8,7 @@ | |||
"resolveJsonModule": true, | |||
"declaration": true, | |||
"declarationDir": "types", | |||
"jsx": "react-native", | |||
"jsx": "react", |
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.
Without this change I'm getting ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
in tests. If we don't want to make this change could we create a tsconfig
for testing or provide some sort of transform to sewing-kit?
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 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.
Yo! Back in the day we had a thing in our sewing-kit config that force changed the jsx compilation in jest.
It got took out because it didn't seem to be needed. I don't know what in your update stuff has made its return required but it's easy to bring back
return mountWithAppProvider( | ||
<ThemeProvider value={context}>{node}</ThemeProvider>, | ||
); | ||
function mergeThemeProviderContext(providedThemeContext: ThemeProviderContext) { |
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 might be overkill, it just saves writing {context: themeProvider: {}}}
every time
@@ -2,13 +2,10 @@ import * as React from 'react'; | |||
import {noop} from '@shopify/javascript-utilities/other'; |
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.
Using mountWithAppProvider
now in this file
} from '../components/AppProvider'; | ||
// eslint-disable-next-line shopify/strict-component-boundaries | ||
import {Provider as FrameProvider, FrameContext} from '../components/Frame'; | ||
// eslint-disable-next-line shopify/strict-component-boundaries |
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.
When we export the return value from React.createContext
we should be able to import this from '../components'
This should be ready for review 🤞 |
Nice one! 🙏 |
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.
Nicely annotated PR. Thanks for putting in the effort to explain.
I've mostly asked a bunch of questions here to help improve mine and others' understanding of what's going on.
I feel comfortable with merging this once CI is 🍏, especially since it is merging into a feature branch.
We'll want to think about a sane strategy for tophatting the v4 branch with all these changes in place. That probably looks like doing some pre-releasing and testing directly in web.
src/components/AppProvider/utilities/createPolarisContext/createPolarisContext.ts
Show resolved
Hide resolved
src/components/AppProvider/utilities/createPolarisContext/tests/createPolarisContext.test.tsx
Show resolved
Hide resolved
? merge(WrappedComponent.contextTypes, polarisAppProviderContextTypes) | ||
: polarisAppProviderContextTypes; | ||
export interface Options { | ||
inScrollable?: boolean; |
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.
withinScrollable
?
src/components/AppProvider/utilities/withAppProvider/withAppProvider.tsx
Show resolved
Hide resolved
@@ -10,13 +10,18 @@ describe('ScrollLock', () => { | |||
showScrollLock: true, | |||
}; | |||
|
|||
setScollLockFalse = () => { | |||
this.setState({showScrollLock: false}); | |||
}; |
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.
Don't we want to avoid setting state in tests?
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.
Changing state within a component through user interactions is ok, however, we want to avoid explicitly setting state with the enzyme API. However, in this case, we may be able to simplify the test by unmounting the wrapper, I'll give that a try.
Add types to test until Remove polaris context namespace Remove frame namespace Export createContext result Small quaility change Rename context wrapper Update themeprovider namespace Add type Add theme context to util Update type name for app provider context Update test utility and revert tsconfig changee
7f8b15b
to
6ee408c
Compare
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 good.
Nothing jumps out at me, though I'm not very familiar with the AppProvider
Convert ScrollTo Remove dropzone context from index Convert navigation Convert combobox Convert withincontext Update frame context Update resourcelist context Update themeprovider context Update appprovider context Touchups changelog Update appprovider context type Rename file
Update context
[v4] Add usePolaris/AppBridge
WHY are these changes introduced?
Sorry for the large pr, a lot of these changes needed to happen at the same time 😅 I tried to keep comments to a minimum since a lot of the stuff repeats.
There was previously an issue with our context types, this required a lot of changes (app bridge, theme, and stickyManager)
A lot of tests needed to change with our new utils. You'll no longer be able to use api's like
setState
andprop
on your mounted component which will promote stronger more valuable tests. Example belowWe no longer have namespaces on Polaris and frame context since we don't have to worry about collisions. This is because we're receiving context from a consumer.
WHAT is this pull request doing?
AppProvider
and associated components to the new context api🎩
A lot of the changes are related to tests and types
A lovely sticky playground from Dan L.
Copy-paste this code in
playground/Playground.tsx
:Notes