Skip to content

Commit

Permalink
Merge branch 'main' of github.com:Mojang/ore-ui into react-18-main
Browse files Browse the repository at this point in the history
  • Loading branch information
marlonicus committed Aug 12, 2024
2 parents e22975d + 193244d commit 88ae92e
Show file tree
Hide file tree
Showing 25 changed files with 678 additions and 270 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ module.exports = {
'import/no-cycle': 'error',
'no-unreachable': 'error',
'no-undef': 'error',
'eqeqeq': 'error',
eqeqeq: 'error',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error',
'react/jsx-uses-react': 'error',
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/benchmarking.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
node-version-file: '.nvmrc'
cache: 'yarn'
- run: yarn install --immutable
- run: cd examples/benchmarking && yarn build && yarn compare mountFacetDomFiber mountReactDom 90
- run: cd examples/benchmarking && yarn build && yarn compare mountFacetDomFiber mountReactDom 91

overhead:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v14.20.0
v20.11.1
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Currently the process of updating the version of the packages is mostly manual. Before you start, first you need to make sure you have the permissions to publish the packages.

- If there is a release branch (see __Release candidate__ below) that hasn't been merged to `main` yet now is the time to do so.
- If there is a release branch (see **Release candidate** below) that hasn't been merged to `main` yet now is the time to do so.
- Make sure that you are logged in into your `npm` account. Use the command `yarn npm login` on the project folder to do this.
- While on the `main` branch.
- Perform a search an replace on all "package.json" files from the old version to the new. (ex `0.1.4` to `0.2.0`).
Expand All @@ -25,4 +25,4 @@ Currently the process of updating the version of the packages is mostly manual.
- Create an annotated git tag by running `git tag -a v0.4.0-rc.0` (replace with the version). The tag message can be the version again.
- Push commit and the tag `git push --follow-tags`.
- Publish the packages by running `yarn publish --tag rc` (it will also build the packages).
- We are currently not doing release notes for release candidates so you are all done! 🎉
- We are currently not doing release notes for release candidates so you are all done! 🎉
10 changes: 5 additions & 5 deletions examples/benchmarking/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
"compare": "ts-node compare.ts"
},
"dependencies": {
"@react-facet/core": "0.5.3",
"@react-facet/dom-fiber": "0.5.3",
"@react-facet/shared-facet": "0.5.3",
"@react-facet/spring": "0.5.3",
"@react-facet/core": "0.6.1",
"@react-facet/dom-fiber": "0.6.1",
"@react-facet/shared-facet": "0.6.1",
"@react-facet/spring": "0.6.1",
"ramda": "^0.27.1",
"react": "18.2.0",
"react-dom": "^18.2.0"
Expand All @@ -28,7 +28,7 @@
"ts-loader": "^9.2.5",
"ts-node": "^10.3.1",
"typescript": "^4.8.2",
"webpack": "^5.51.1",
"webpack": "^5.91.0",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.0.0"
}
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-facet/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"homepage": "https://react-facet.mojang.com/",
"bugs": "https://github.com/Mojang/ore-ui/issues",
"license": "MIT",
"version": "0.5.3",
"version": "0.6.1",
"main": "src/index.ts",
"publishConfig": {
"main": "dist/index.js",
Expand All @@ -44,7 +44,7 @@
"react": "^18.2.0"
},
"devDependencies": {
"@react-facet/dom-fiber-testing-library": "0.5.3",
"@react-facet/dom-fiber-testing-library": "0.6.1",
"@types/react": "^18.0.8",
"@types/react-reconciler": "^0.28.0",
"@types/rimraf": "^3",
Expand Down
18 changes: 18 additions & 0 deletions packages/@react-facet/core/src/components/Map.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,21 @@ it('updates only items that have changed', () => {
expect(mock).toHaveBeenCalledTimes(1)
expect(mock).toHaveBeenCalledWith({ a: '6' })
})

it('provides the length of the array', () => {
const data = createFacet({
initialValue: [{ a: '1' }, { a: '2' }, { a: '3' }],
})

const ExampleContent = ({ index, length }: { index: number; length: number }) => {
return <>{length === index + 1 && <div data-testid={'length'}>{length}</div>}</>
}

const Example = () => {
return <Map array={data}>{(_, index, length) => <ExampleContent index={index} length={length} />}</Map>
}

const { getByTestId } = render(<Example />)

expect(getByTestId('length')).toHaveTextContent('3')
})
25 changes: 14 additions & 11 deletions packages/@react-facet/core/src/components/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { EqualityCheck, Facet, NO_VALUE } from '../types'

export type MapProps<T> = {
array: Facet<T[]>
children: (item: Facet<T>, index: number) => ReactElement | null
children: (item: Facet<T>, index: number, length: number) => ReactElement | null
equalityCheck?: EqualityCheck<T>
}

export const Map = <T,>({ array, children, equalityCheck }: MapProps<T>) => {
// When mounting lists, we always want to defer
const countValue = useFacetUnwrap(useFacetMap((array) => array.length, [], [array]))
const lengthValue = useFacetUnwrap(useFacetMap((array) => array.length, [], [array]))
const lengthNumber = lengthValue !== NO_VALUE ? lengthValue : 0

return (
<>
Expand All @@ -23,13 +23,14 @@ export const Map = <T,>({ array, children, equalityCheck }: MapProps<T>) => {
key={index}
arrayFacet={array}
index={index}
length={lengthNumber}
equalityCheck={equalityCheck}
children={children}
/>
) : (
<MapChild<T> key={index} arrayFacet={array} index={index} children={children} />
<MapChild<T> key={index} arrayFacet={array} index={index} length={lengthNumber} children={children} />
),
countValue !== NO_VALUE ? countValue : 0,
lengthNumber,
)}
</>
)
Expand All @@ -38,11 +39,12 @@ export const Map = <T,>({ array, children, equalityCheck }: MapProps<T>) => {
type MapChildMemoProps<T> = {
arrayFacet: Facet<T[]>
index: number
children: (item: Facet<T>, index: number) => ReactElement | null
length: number
children: (item: Facet<T>, index: number, length: number) => ReactElement | null
equalityCheck: EqualityCheck<T>
}

const MapChildMemo = <T,>({ arrayFacet, index, children, equalityCheck }: MapChildMemoProps<T>) => {
const MapChildMemo = <T,>({ arrayFacet, index, length, children, equalityCheck }: MapChildMemoProps<T>) => {
const childFacet = useFacetMemo(
(array) => {
if (index < array.length) return array[index]
Expand All @@ -52,16 +54,17 @@ const MapChildMemo = <T,>({ arrayFacet, index, children, equalityCheck }: MapChi
[arrayFacet],
equalityCheck,
)
return children(childFacet, index)
return children(childFacet, index, length)
}

type MapChildProps<T> = {
arrayFacet: Facet<T[]>
index: number
children: (item: Facet<T>, index: number) => ReactElement | null
length: number
children: (item: Facet<T>, index: number, length: number) => ReactElement | null
}

const MapChild = <T,>({ arrayFacet, index, children }: MapChildProps<T>) => {
const MapChild = <T,>({ arrayFacet, index, length, children }: MapChildProps<T>) => {
const childFacet = useFacetMap(
(array) => {
if (index < array.length) return array[index]
Expand All @@ -71,7 +74,7 @@ const MapChild = <T,>({ arrayFacet, index, children }: MapChildProps<T>) => {
[arrayFacet],
)

return children(childFacet, index)
return children(childFacet, index, length)
}

interface TimesFn<T> {
Expand Down
16 changes: 15 additions & 1 deletion packages/@react-facet/core/src/hooks/useFacetUnwrap.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ it('does not trigger a re-render when changing a facet from undefined to undefin
})

it('supports custom equality checks', () => {
const value = {}
const value = { prop: 'initial' }
const demoFacet = createFacet({ initialValue: value })

// Dummy equality check that always returns its not equal
Expand Down Expand Up @@ -267,4 +267,18 @@ it('supports custom equality checks', () => {
expect(check).toHaveBeenCalledTimes(1) // but the check should be executed
expect(check).toHaveBeenCalledWith(value) // passing the value (which should be the same)
expect(renderedMock).toHaveBeenCalledTimes(1) // and since the equality check always returns "false", we have a render

jest.clearAllMocks()

const newValue = { prop: 'new' }

// If we update with a new object,
act(() => {
demoFacet.set(newValue)
})

expect(equalityCheck).toHaveBeenCalledTimes(0) // equality check was already initialized
expect(check).toHaveBeenCalledTimes(1) // but the check should be executed
expect(check).toHaveBeenCalledWith(newValue) // passing the new value
expect(renderedMock).toHaveBeenCalledTimes(1) // and since the equality check always returns "false", we have a render
})
2 changes: 1 addition & 1 deletion packages/@react-facet/core/src/hooks/useFacetUnwrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function useFacetUnwrap<T extends Value>(
return { value }
}

if (previousValue !== NO_VALUE && isEqual(previousValue)) {
if (previousValue !== NO_VALUE && isEqual(value)) {
return previousState
}

Expand Down
18 changes: 17 additions & 1 deletion packages/@react-facet/core/src/mapFacets/mapFacetArrayCached.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,25 @@ export function mapFacetArrayCached<M>(
): Facet<M> {
const initialValues = facets.map((facet) => facet.get())
const hasAllValues = initialValues.reduce<boolean>((prev, curr) => prev && curr !== NO_VALUE, true)
return createFacet<M>({
const cachedFacet = createFacet<M>({
// pass the equalityCheck to the mapIntoObserveArray to prevent even triggering the observable
startSubscription: mapIntoObserveArray(facets, fn, equalityCheck),
initialValue: hasAllValues ? fn(...initialValues) : NO_VALUE,
})

return {
get: () => {
const cachedValue = cachedFacet.get()
if (cachedValue !== NO_VALUE) return cachedValue

const dependencyValues = facets.map((facet) => facet.get())
const hasAllValues = dependencyValues.reduce<boolean>((acc, value) => acc && value !== NO_VALUE, true)
if (!hasAllValues) return NO_VALUE

const mappedValue = fn(...dependencyValues)
if (mappedValue !== NO_VALUE) cachedFacet.set(mappedValue)
return mappedValue
},
observe: cachedFacet.observe,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ export function mapFacetArrayLightweight<M>(
): Facet<M> {
return {
get: () => {
const values = facets.map((facet) => facet.get())
const hasAllValues = values.reduce<boolean>((acc, value) => acc && value !== NO_VALUE, true)
const dependencyValues = facets.map((facet) => facet.get())
const hasAllValues = dependencyValues.reduce<boolean>((acc, value) => acc && value !== NO_VALUE, true)
if (!hasAllValues) return NO_VALUE

return fn(...values)
return fn(...dependencyValues)
},
observe: mapIntoObserveArray(facets, fn, equalityCheck),
}
Expand Down
23 changes: 19 additions & 4 deletions packages/@react-facet/core/src/mapFacets/mapFacetSingleCached.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,29 @@ import { mapIntoObserveSingle } from './mapIntoObserveSingle'
import { createFacet } from '../facet'

export function mapFacetSingleCached<T, M>(
facets: Facet<T>,
facet: Facet<T>,
fn: (value: T) => M | NoValue,
equalityCheck?: EqualityCheck<M>,
): Facet<M> {
const initialValue = facets.get()
return createFacet<M>({
const initialValue = facet.get()
const cachedFacet = createFacet<M>({
// pass the equalityCheck to the mapIntoObserveSingle to prevent even triggering the observable
startSubscription: mapIntoObserveSingle(facets, fn, equalityCheck),
startSubscription: mapIntoObserveSingle(facet, fn, equalityCheck),
initialValue: initialValue !== NO_VALUE ? fn(initialValue) : NO_VALUE,
})

return {
get: () => {
const cachedValue = cachedFacet.get()
if (cachedValue !== NO_VALUE) return cachedValue

const dependencyValue = facet.get()
if (dependencyValue === NO_VALUE) return NO_VALUE

const mappedValue = fn(dependencyValue)
if (mappedValue !== NO_VALUE) cachedFacet.set(mappedValue)
return mappedValue
},
observe: cachedFacet.observe,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ export function mapFacetSingleLightweight<T, M>(
): Facet<M> {
return {
get: () => {
const value = facet.get()
if (value === NO_VALUE) return NO_VALUE
const dependencyValue = facet.get()
if (dependencyValue === NO_VALUE) return NO_VALUE

return fn(value)
return fn(dependencyValue)
},

observe: mapIntoObserveSingle(facet, fn, equalityCheck),
Expand Down
61 changes: 61 additions & 0 deletions packages/@react-facet/core/src/mapFacets/mapFacets.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,67 @@ describe('mapFacetsCached WITH an initial value', () => {
// check that the map was called just once
expect(mapFunction).toHaveBeenCalledTimes(1)
})

it('gets NO_VALUE as a value from a single source if it also has NO_VALUE', () => {
const mapFunction = jest.fn().mockReturnValue('dummy')
const sourceFacet = createFacet({ initialValue: NO_VALUE })
const mapFacet = mapFacetsCached([sourceFacet], mapFunction, () => () => false)

expect(mapFacet.get()).toBe(NO_VALUE)
})

it('gets NO_VALUE as a value from multiple sources if they also have NO_VALUE', () => {
const mapFunction = jest.fn().mockReturnValue('dummy')
const sourceAFacet = createFacet({ initialValue: 'initial value' })
const sourceBFacet = createFacet({ initialValue: NO_VALUE })
const mapFacet = mapFacetsCached([sourceAFacet, sourceBFacet], mapFunction, () => () => false)

expect(mapFacet.get()).toBe(NO_VALUE)
})

it('can get the mapped value from a single source before any subscription', () => {
const mapFunction = jest.fn().mockReturnValue('dummy')
const sourceFacet = createFacet({ initialValue: 'initial value' })
const mapFacet = mapFacetsCached([sourceFacet], mapFunction, () => () => false)

expect(mapFacet.get()).toBe('dummy')
})

it('can get the mapped value from multiple sources before any subscription', () => {
const mapFunction = jest.fn().mockReturnValue('dummy')
const sourceAFacet = createFacet({ initialValue: 'initial value' })
const sourceBFacet = createFacet({ initialValue: 'initial value' })
const mapFacet = mapFacetsCached([sourceAFacet, sourceBFacet], mapFunction, () => () => false)

expect(mapFacet.get()).toBe('dummy')
})

it('caches calls to the mapFunction through a get call before any subscription, given a single source', () => {
const mapFunction = jest.fn().mockReturnValue('dummy')
const sourceFacet = createFacet({ initialValue: 'initial value' })
const mapFacet = mapFacetsCached([sourceFacet], mapFunction, () => () => false)

expect(mapFacet.get()).toBe('dummy')
expect(mapFunction).toHaveBeenCalledTimes(1)

mapFunction.mockClear()
expect(mapFacet.get()).toBe('dummy')
expect(mapFunction).not.toHaveBeenCalled()
})

it('caches calls to the mapFunction through a get call before any subscription, given multiple sources', () => {
const mapFunction = jest.fn().mockReturnValue('dummy')
const sourceAFacet = createFacet({ initialValue: 'initial value' })
const sourceBFacet = createFacet({ initialValue: 'initial value' })
const mapFacet = mapFacetsCached([sourceAFacet, sourceBFacet], mapFunction, () => () => false)

expect(mapFacet.get()).toBe('dummy')
expect(mapFunction).toHaveBeenCalledTimes(1)

mapFunction.mockClear()
expect(mapFacet.get()).toBe('dummy')
expect(mapFunction).not.toHaveBeenCalled()
})
})

describe('mapFacetsLightweight', () => {
Expand Down
8 changes: 4 additions & 4 deletions packages/@react-facet/dom-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"homepage": "https://react-facet.mojang.com/",
"bugs": "https://github.com/Mojang/ore-ui/issues",
"license": "MIT",
"version": "0.5.3",
"version": "0.6.1",
"main": "src/index.tsx",
"publishConfig": {
"main": "dist/index.js",
Expand All @@ -41,12 +41,12 @@
"prepublish": "yarn build"
},
"peerDependencies": {
"@react-facet/core": "0.5.3",
"@react-facet/core": "0.6.1",
"react": "^18.2.0"
},
"devDependencies": {
"@react-facet/core": "0.5.3",
"@react-facet/dom-fiber-testing-library": "0.5.3",
"@react-facet/core": "0.6.1",
"@react-facet/dom-fiber-testing-library": "0.6.1",
"@testing-library/jest-dom": "^5.16.5",
"@types/react": "^18.0.8",
"@types/rimraf": "^3",
Expand Down
Loading

0 comments on commit 88ae92e

Please sign in to comment.