Compare commits

...

14 Commits

Author SHA1 Message Date
zplata
965507ac34 chore: release 2.0.0 2022-04-22 22:04:35 +00:00
Zach Plata
74b791ffa4 Fix: Authenticate registry before doing npm release dryrun 2022-04-22 15:02:27 -07:00
Zach Plata
2853851d52 Fix: Do npm install instead 2022-04-22 14:54:53 -07:00
Zach Plata
4f37bbdb76 fix up test 2022-04-22 14:48:17 -07:00
Zach Plata
39f1990eac Maint: Bump wasm dependencies and add major flags for the publish script 2022-04-22 14:48:17 -07:00
Zach Plata
bb7d3e4e6c Feat: Setup workflow to npm publish 2 new rive-react packages 2022-04-22 14:48:17 -07:00
Zach Plata
7f2bd76eb3 Fix up test 2022-04-22 14:48:17 -07:00
Zach Plata
5c0b9cd613 Breaking: Spread non-style props onto canvas element instead of containing div 2022-04-22 14:48:17 -07:00
Zach Plata
92dd811948 fix version compare function 2022-04-22 14:48:17 -07:00
Zach Plata
4429be44f2 Change rive-react-* references to be the shortened namespace-named convention 2022-04-22 14:48:17 -07:00
Zach Plata
9398e0d74e Update tests 2022-04-22 14:48:17 -07:00
Zach Plata
563dca3608 Move to using @rive-app/canvas as the backing renderer and update scripts accordingly. 2022-04-22 14:48:17 -07:00
Zach Plata
1da73aac05 Only run the publish job on PR merges to main 2022-04-22 14:48:17 -07:00
Zach Plata
5c4336b84f Update: Adding build scripts to prepare for split of rive-react into 2 package derivatives for canvas and webgl 2022-04-22 14:48:17 -07:00
23 changed files with 332 additions and 66 deletions

View File

@@ -5,8 +5,37 @@ on:
branches:
- main
jobs:
merge_job:
determine_version:
name: Determine the next build version
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
outputs:
version: ${{ steps.echo_version.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install dependencies
run: npm install
working-directory: ./
- name: Git config
run: |
git config --local user.email 'hello@rive.app'
git config --local user.name ${{ github.actor }}
- name: Authenticate with registry
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- id: determine_version
name: Get Version
run: npm run release -- major --ci --release-version | tail -n 1 > RELEASE_VERSION
working-directory: ./
env:
GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }}
- id: echo_version
run: echo "::set-output name=version::$(cat ./RELEASE_VERSION)"
merge_job:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
needs: [determine_version]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
@@ -21,15 +50,30 @@ jobs:
run: npm run lint
- name: Run Tests
run: npm test
- name: Build
- name: Build main dist
run: npm run build
- name: Copy separated builds
run: ./scripts/build.sh
- name: Copy package jsons to separate react outputs
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: ./scripts/setup_all_packages.sh
- name: Bump Versions of react outputs
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
RELEASE_VERSION: ${{ needs.determine_version.outputs.version }}
run: ./scripts/bump_all_versions.sh
- name: Git config
run: |
git config --local user.email 'hello@rive.app'
git config --local user.name ${{ github.actor }}
- name: Authenticate with registry
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- name: Release
- name: Release rive-react
env:
GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }}
run: npm run release
run: npm run release -- major --ci
- name: Release @rive-app/react-* variants
env:
GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }}
run: ./scripts/publish_all.sh

View File

@@ -4,16 +4,9 @@ All notable changes to this project will be documented in this file. Dates are d
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### [v1.0.7](https://github.com/rive-app/rive-react/compare/v1.0.6...v1.0.7)
#### [v2.0.0](https://github.com/rive-app/rive-react/compare/v1.0.5...v2.0.0)
- Maint: Allow peer dep React to use v18.0+ [`464fa97`](https://github.com/rive-app/rive-react/commit/464fa97c207e780d31866ecad0437dc16aefd1f2)
#### [v1.0.6](https://github.com/rive-app/rive-react/compare/v1.0.5...v1.0.6)
> 14 April 2022
- chore: release 1.0.6 [`ac01b7e`](https://github.com/rive-app/rive-react/commit/ac01b7ec691c435371f9553df88144c75af18ed4)
- Maint: Bump webgl dependency up to 1.0.39 [`cd9baef`](https://github.com/rive-app/rive-react/commit/cd9baef25003b491d40d5649ba1d3b5f88bce870)
- Fix: Authenticate registry before doing npm release dryrun [`74b791f`](https://github.com/rive-app/rive-react/commit/74b791ffa4042c98aa5d5e02ef03e9df2eb6dd16)
#### [v1.0.5](https://github.com/rive-app/rive-react/compare/v1.0.4...v1.0.5)

View File

@@ -22,30 +22,6 @@ npm i --save rive-react
_Note: This library is using React hooks so the minimum version required for both react and react-dom is 16.8.0._
### Migrating from v 0.0.x to 1.x.x
Starting in v 1.0.0, we've migrated from wrapping around the `@rive-app/canvas` runtime (which uses the `CanvasRendereringContext2D` renderer) to the `@rive-app/webgl` runtime (which uses the WebGL renderer). The high-level API doesn't require any change to upgrade, but there are some notes to consider about the backing renderer.
The backing `WebGL` runtime allows for best performance across all devices, as well as support for some features that are not supported in the `canvas` renderer runtime. To allow the `react` runtime to support some of the newer features in Rive, we needed to switch the `rive-react` backing runtime to `@rive-app/webgl`.
One note about this switch is that some browsers may limit the number of concurrent WebGL contexts. For example, Chrome may only support up to 16 contexts concurrently. We pass a property called `useOffscreenRenderer` set to true to the backing runtime when instantiating Rive by default, which helps to manage the lifecycle of the `canvas` with a single offscreen `WebGL` context, even if there are many Rive animations on the screen (i.e 16+). If you need a single `WebGL` context per Rive animation/instance, pass in the `useOffscreenRenderer` property set to `false` in the `useRive` options, or as a prop in the default export component from this runtime. See below for an example:
```js
const {rive, RiveComponent} = useRive({
src: 'foo.riv',
}, {
// Default (you don't need to set this)
useOffscreenRenderer: true,
// To override and use one context per Rive instance, uncomment and use the line below
// useOffscreenRenderer: false,
});
// or you can override the flag in JSX via props
return (
<Rive src="foo.riv" useOffscreenRenderer={false} />
);
```
## Usage
### Component
@@ -68,7 +44,17 @@ export default Example;
- `artboard`: _(optional)_ Name to display.
- `animations`: _(optional)_ Name or list of names of animtions to play.
- `layout`: _(optional)_ Layout object to define how animations are displayed on the canvas. See [Rive.js](https://github.com/rive-app/rive-wasm#layout) for more details.
- _All attributes and eventHandlers that can be passed to a `div` element can also be passed to the `Rive` component and used in the same manner._
- _All attributes and eventHandlers that can be passed to a `canvas` element can also be passed to the `Rive` component and used in the same manner._
#### Styles and Classes
When rendering out a Rive component, in the DOM, it will show as a `<div>` element that contains the `<canvas>` element that powers the Rive animations. The purpose of the `<div>` element is to help control the sizing of the component. By default, the container has the following styles set on the `style` attribute:
```css
width: 100%;
height: 100%;
```
If you decide to pass in a `className` to the Rive component, you will override these attributes, and you will need to either set these style attributes in your CSS associated with that `className`, or set your own sizing preferences.
### useRive Hook
@@ -102,7 +88,7 @@ export default Example;
#### Return Values
- `RiveComponent`: A Component that can be used to display your .riv file. This component accepts the same attributes and event handlers as a `div` element.
- `RiveComponent`: A Component that can be used to display your .riv file. This component accepts the same attributes and event handlers as a `canvas` element.
- `rive`: A Rive.js `Rive` object. This will return as null until the .riv file has fully loaded.
- `canvas`: HTMLCanvasElement object, on which the .riv file is rendering.
- `setCanvasRef`: A callback ref that can be passed to your own canvas element, if you wish to have control over the rendering of the Canvas element.
@@ -178,3 +164,39 @@ A Rive.js `stateMachineInput` object.
## Examples
The [examples](examples) shows a number of different ways to use Rive React. See the instructions for each example to run locally.
## Migration notes
### Migrating from version 0.0.x to 1.x.x
Starting in v 1.0.0, we've migrated from wrapping around the `@rive-app/canvas` runtime (which uses the `CanvasRendereringContext2D` renderer) to the `@rive-app/webgl` runtime (which uses the WebGL renderer). The high-level API doesn't require any change to upgrade, but there are some notes to consider about the backing renderer.
The backing `WebGL` runtime allows for best performance across all devices, as well as support for some features that are not supported in the `canvas` renderer runtime. To allow the `react` runtime to support some of the newer features in Rive, we needed to switch the `rive-react` backing runtime to `@rive-app/webgl`.
One note about this switch is that some browsers may limit the number of concurrent WebGL contexts. For example, Chrome may only support up to 16 contexts concurrently. We pass a property called `useOffscreenRenderer` set to true to the backing runtime when instantiating Rive by default, which helps to manage the lifecycle of the `canvas` with a single offscreen `WebGL` context, even if there are many Rive animations on the screen (i.e 16+). If you need a single `WebGL` context per Rive animation/instance, pass in the `useOffscreenRenderer` property set to `false` in the `useRive` options, or as a prop in the default export component from this runtime. See below for an example:
```js
const {rive, RiveComponent} = useRive({
src: 'foo.riv',
}, {
// Default (you don't need to set this)
useOffscreenRenderer: true,
// To override and use one context per Rive instance, uncomment and use the line below
// useOffscreenRenderer: false,
});
// or you can override the flag in JSX via props
return (
<Rive src="foo.riv" useOffscreenRenderer={false} />
);
```
### Migrating from version 1.x.x to 2.x.x
#### Package split
In most cases, you may be able to migrate safely. We are mainly enabling the React runtime to work with both backing renderers `@rive-app/webgl` and `@rive-app/canvas`, such that you can use either `@rive-app/react-canvas` or `@rive-app/react-webgl` as the dependency in your React applications. Another change that is mostly internal is that by default, `rive-react` will now use `@rive-app/canvas` (as opposed to `@rive-app/webgl`) to wrap around, as it currently yields the fastest performance across devices. Therefore, **we recommend installing `@rive-app/react-canvas` in your applicaions**. However, if you need a WebGL backing renderer, you may want to use `@rive-app/react-webgl`.
#### Classes, styles, and component props
Starting in v2.0, we introduce one breaking change where any non-style props set on the `RiveComponent` (i.e `aria-*`, `role`, etc.) will be set on the inner `<canvas>` element. Previously, all extra props would be set onto the containing `<div>` element. Both the `className` and `style` props will continue to be set on the `<div>` element that wraps the canvas, as this dictates the sizing of the Rive component.

View File

@@ -0,0 +1,3 @@
# @rive-app/react-canvas
Output for `rive-react` using the backing `@rive-app/canvas` JS runtime

View File

@@ -0,0 +1,3 @@
# @rive-app/react-webgl
Output for `rive-react` using the backing `@rive-app/webgl` JS runtime

View File

@@ -1,6 +1,6 @@
{
"name": "rive-react",
"version": "1.0.7",
"version": "2.0.0",
"description": "React wrapper around the rive-js library",
"main": "dist/index.js",
"typings": "dist/types/index.d.ts",
@@ -27,7 +27,8 @@
},
"homepage": "https://github.com/rive-app/rive-react#readme",
"dependencies": {
"@rive-app/webgl": "1.0.39"
"@rive-app/canvas": "1.0.46",
"@rive-app/webgl": "1.0.43"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"

18
scripts/build.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/bash
set -e
# Copy the build to each react-variant build for npm release
cp -r ./dist ./npm/react-webgl
cp -r ./dist ./npm/react-canvas
echo "Replacing the canvas with webgl references in react-webgl"
pushd ./npm/react-webgl/dist
if [[ "$OSTYPE" == "darwin"* ]]; then
find . -type f -name "*.ts" -print0 | xargs -0 sed -i '' -e 's/@rive-app\/canvas/@rive-app\/webgl/g'
find . -type f -name "*.js" -print0 | xargs -0 sed -i '' -e 's/@rive-app\/canvas/@rive-app\/webgl/g'
else
find . -type f -name "*.ts" -print0 | xargs -0 sed -i -e 's/@rive-app\/canvas/@rive-app\/webgl/g'
find . -type f -name "*.js" -print0 | xargs -0 sed -i -e 's/@rive-app\/canvas/@rive-app\/webgl/g'
fi
popd

11
scripts/bump_all_versions.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
set -e
# Bump the version number of every npm module in the npm folder.
for dir in ./npm/*; do
pushd $dir > /dev/null
repo_name=`echo $dir | sed 's:.*/::' | sed 's/_/-/g'`
echo Bumping version of $repo_name
../../scripts/bump_version.sh $repo_name $RELEASE_VERSION
popd > /dev/null
done

12
scripts/bump_version.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
# Bumps the version of a single npm module found in the current working
# directory. Call bump_version.sh from the path with package.json in it.
set -e
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
# RELEASE_VERSION will come from an env variable passed in from the GH action workflow
node $SCRIPT_DIR/nextVersion.js "$RELEASE_VERSION" `pwd`
# Replace the dist package json with the newly trimmed one
cp -f ./package.json ./dist/package.json

33
scripts/nextVersion.js Normal file
View File

@@ -0,0 +1,33 @@
const fs = require('fs');
const path = process.argv[3];
const package = require(path + '/package.json');
const currentVersion = package.version;
const nextVersion = process.argv[2].trim().replace(/\'/g, '"');
if (!nextVersion || nextVersion === currentVersion) {
throw new Error('Next version is not defined or is a version that already exists');
}
// Returns -1 if first is less than second, 1 if first is greater than second, otherwise 0 if equal.
function compareVersion(first, second) {
// Assumption: only numbers in our versions.
const firstParts = first.split('.').map((value) => parseInt(value));
const secondParts = second.split('.').map((value) => parseInt(value));
for (let i = 0; i < Math.max(firstParts.length, secondParts.length); i++) {
const first = i < firstParts.length ? firstParts[i] : 0;
const second = i < secondParts.length ? secondParts[i] : 0;
if (first < second) {
return -1;
}
if (second < first) {
return 1;
}
}
return 0;
}
if (compareVersion(currentVersion, nextVersion) <= 0) {
package.version = nextVersion;
fs.writeFileSync(path + '/package.json', JSON.stringify(package, null, 2));
}

10
scripts/publish_all.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/bash
set -e
# Bump the version number of every npm module in the npm folder.
for dir in ./npm/*; do
pushd $dir > /dev/null
echo Publishing `echo $dir | sed 's:.*/::'`
npm publish --access public
popd > /dev/null
done

21
scripts/setup_all_packages.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
set -e
echo "Copying package.json to rive-react npm package folders"
cp package.json npm/react-canvas
cp package.json npm/react-webgl
# Bump the version number of every npm module in the npm folder.
for dir in ./npm/*; do
echo $dir
pushd $dir > /dev/null
echo $dir
repo_name=`echo $dir | sed 's:.*/::' | sed 's/_/-/g'`
echo Setting package.json on npm packages
echo $repo_name
../../scripts/setup_package.sh $repo_name
echo Finished setting up package.json
popd > /dev/null
done

6
scripts/setup_package.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
set -e
# Setup the package.json for a given npm module
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
node $SCRIPT_DIR/trimPackageJson.js `pwd` "$1"

View File

@@ -0,0 +1,22 @@
const fs = require('fs');
const path = process.argv[2];
const npmPackageSplit = process.argv[3].split('-');
const renderer = npmPackageSplit[npmPackageSplit.length - 1];
const package = require(path + '/package.json');
function trimNpmPackage() {
package.name = `@rive-app/react-${renderer}`;
package.description = `React wrapper around the @rive-app/${renderer} library`;
const webDependencyName = `@rive-app/${renderer}`;
const canvasDep = package.dependencies[webDependencyName];
package.dependencies = {
[webDependencyName]: canvasDep,
};
delete package.devDependencies;
delete package.scripts;
fs.writeFileSync(path + '/package.json', JSON.stringify(package, null, 2));
}
if (renderer) {
trimNpmPackage();
}

View File

@@ -24,7 +24,7 @@ window.IntersectionObserver = class IntersectionObserver {
unobserve() {}
};
jest.mock('@rive-app/webgl', () => ({
jest.mock('@rive-app/canvas', () => ({
Rive: jest.fn().mockImplementation(() => ({
on: jest.fn(),
stop: jest.fn(),

View File

@@ -1,4 +1,4 @@
import { Layout } from '@rive-app/webgl';
import { Layout } from '@rive-app/canvas';
import React, { ComponentProps } from 'react';
import useRive from '../hooks/useRive';
@@ -17,7 +17,7 @@ const Rive = ({
layout,
useOffscreenRenderer = true,
...rest
}: RiveProps & ComponentProps<'div'>) => {
}: RiveProps & ComponentProps<'canvas'>) => {
const params = {
src,
artboard,

View File

@@ -6,7 +6,7 @@ import React, {
ComponentProps,
RefCallback,
} from 'react';
import { Rive, EventType } from '@rive-app/webgl';
import { Rive, EventType } from '@rive-app/canvas';
import {
UseRiveParameters,
UseRiveOptions,
@@ -23,20 +23,23 @@ type RiveComponentProps = {
function RiveComponent({
setContainerRef,
setCanvasRef,
className = '',
style,
...rest
}: RiveComponentProps & ComponentProps<'div'>) {
}: RiveComponentProps & ComponentProps<'canvas'>) {
const containerStyle = {
width: '100%',
height: '100%',
...style,
};
return (
<div
ref={setContainerRef}
style={'className' in rest ? undefined : containerStyle}
{...rest}
className={className}
{...(!className && { style: containerStyle })}
>
<canvas ref={setCanvasRef} style={{ verticalAlign: 'top' }} />
<canvas ref={setCanvasRef} style={{ verticalAlign: 'top' }} {...rest} />
</div>
);
}
@@ -235,15 +238,18 @@ export default function useRive(
}
}, [animations, rive]);
const Component = useCallback((props: ComponentProps<'div'>): JSX.Element => {
return (
<RiveComponent
setContainerRef={setContainerRef}
setCanvasRef={setCanvasRef}
{...props}
/>
);
}, []);
const Component = useCallback(
(props: ComponentProps<'canvas'>): JSX.Element => {
return (
<RiveComponent
setContainerRef={setContainerRef}
setCanvasRef={setCanvasRef}
{...props}
/>
);
},
[]
);
return {
canvas: canvasRef.current,

View File

@@ -1,5 +1,5 @@
import { useState, useEffect } from 'react';
import { Rive, StateMachineInput } from '@rive-app/webgl';
import { Rive, StateMachineInput } from '@rive-app/canvas';
/**
* Custom hook for fetching a stateMachine input from a rive file.

View File

@@ -5,4 +5,4 @@ import useStateMachineInput from './hooks/useStateMachineInput';
export default Rive;
export { useRive, useStateMachineInput };
export { RiveState, UseRiveParameters, UseRiveOptions } from './types';
export * from '@rive-app/webgl';
export * from '@rive-app/canvas';

View File

@@ -1,5 +1,5 @@
import { RefCallback, ComponentProps } from 'react';
import { Rive, RiveParameters } from '@rive-app/webgl';
import { Rive, RiveParameters } from '@rive-app/canvas';
export type UseRiveParameters = Partial<Omit<RiveParameters, 'canvas'>> | null;
@@ -29,5 +29,5 @@ export type RiveState = {
setCanvasRef: RefCallback<HTMLCanvasElement>;
setContainerRef: RefCallback<HTMLElement>;
rive: Rive | null;
RiveComponent: (props: ComponentProps<'div'>) => JSX.Element;
RiveComponent: (props: ComponentProps<'canvas'>) => JSX.Element;
};

33
test/Rive.test.tsx Normal file
View File

@@ -0,0 +1,33 @@
import React from 'react';
import RiveComponent from '../src/components/Rive';
import {render} from '@testing-library/react'
jest.mock('@rive-app/canvas', () => ({
Rive: jest.fn().mockImplementation(() => ({
on: jest.fn(),
stop: jest.fn(),
})),
Layout: jest.fn(),
Fit: {
Cover: 'cover',
},
Alignment: {
Center: 'center',
},
EventType: {
Load: 'load',
},
StateMachineInputType: {
Number: 1,
Boolean: 2,
Trigger: 3,
},
}));
describe('Rive Component', () => {
it('renders the component as a canvas and a div wrapper', () => {
const {container, getByLabelText} = render(<RiveComponent src="foo.riv" className="container-styles" aria-label="Foo label" />);
expect(container.firstChild).toHaveClass('container-styles');
expect(getByLabelText('Foo label').tagName).toEqual('CANVAS');
});
});

View File

@@ -1,10 +1,12 @@
import React from 'react';
import { mocked } from 'jest-mock';
import { renderHook, act } from '@testing-library/react-hooks';
import useRive from '../src/hooks/useRive';
import * as rive from '@rive-app/webgl';
import * as rive from '@rive-app/canvas';
import { render } from '@testing-library/react';
jest.mock('@rive-app/webgl', () => ({
jest.mock('@rive-app/canvas', () => ({
Rive: jest.fn().mockImplementation(() => ({
on: jest.fn(),
stop: jest.fn(),
@@ -308,4 +310,30 @@ describe('useRive', () => {
expect(stopMock).toBeCalledWith(['light']);
expect(playMock).toBeCalledWith('dark');
});
it('does not set styles if className is passed in for the canvas container', async () => {
const params = {
src: 'file-src',
};
const riveMock = {
on: (_: string, cb: () => void) => cb(),
stop: jest.fn(),
stopRendering: jest.fn(),
};
// @ts-ignore
mocked(rive.Rive).mockImplementation(() => riveMock);
const canvasSpy = document.createElement('canvas');
const { result } = renderHook(() => useRive(params));
await act(async () => {
result.current.setCanvasRef(canvasSpy);
});
const {RiveComponent: RiveTestComponent} = result.current;
const {container} = render(<RiveTestComponent className="rive-test-clas" style={{width: '50%'}} />);
expect(container.firstChild).not.toHaveStyle('width: 50%');
});
});

View File

@@ -2,9 +2,9 @@ import { mocked } from 'jest-mock';
import { renderHook } from '@testing-library/react-hooks';
import useStateMachineInput from '../src/hooks/useStateMachineInput';
import {Rive, StateMachineInput} from '@rive-app/webgl';
import {Rive, StateMachineInput} from '@rive-app/canvas';
jest.mock('@rive-app/webgl', () => ({
jest.mock('@rive-app/canvas', () => ({
Rive: jest.fn().mockImplementation(() => ({
on: jest.fn(),
stop: jest.fn(),