Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Package CI

on:
pull_request:
push:
branches:
- master
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
package:
name: React Native package
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Set up Node 24.15.0
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 24.15.0

- name: Install Corepack bootstrap
run: npm install --global corepack@0.36.0

- name: Enable pinned Yarn
run: |
corepack enable
yarn --version | grep -Fx '4.5.1'

- name: Resolve Yarn cache
id: yarn-cache
shell: bash
run: echo "path=$(yarn config get cacheFolder)" >> "$GITHUB_OUTPUT"

- name: Cache Yarn packages
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: ${{ steps.yarn-cache.outputs.path }}
key: yarn-${{ runner.os }}-node-24.15.0-${{ hashFiles('yarn.lock') }}
restore-keys: |
yarn-${{ runner.os }}-node-24.15.0-

- name: Install immutable dependencies
run: yarn install --immutable

- name: Run package release preflight
run: yarn release:check

- name: Upload informational Jest coverage
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: react-native-awesome-button-coverage
path: coverage/
if-no-files-found: ignore
retention-days: 14
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ yarn-debug.log
yarn-error.log
.yarn/*
demo/.yarn/
coverage/
temp/
*.tgz

# BUCK
buck-out/
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Changelog

## Unreleased

- Added the typed `buttonStyle`, `containerStyle`, `faceHeight`, `autoWidth`, eventless `onLongPressAction`, accessibility text, and canonical `x` compatibility bridges without changing 3.1.0 legacy meanings.
- Hardened callback freshness, cancellation, one-shot progress completion, teardown, Reduced Motion, large-text, RTL, and theme-transition ownership.
- Added package-owned compatibility, lifecycle, sizing, accessibility, normalization, and theme regressions.
- Added deterministic release preflight, API-report, documentation-coverage, package-shape, coverage, and CI configuration. These gates do not publish the package.
- Preserved explicitly requested flat visual styling while disabled; disabled state still blocks activation.
- Adopted the shared grapheme-aware text-transition timeline, per-frame native fit checks, 30% width/text choreography, constrained single-line fallback, stable accessibility identity, and Reduced Motion settlement.
- Prevented non-scrambled auto-width growth from publishing a longer label before the face can fit it; shrink still swaps the shorter label before contracting.
- Hardened final label settlement with conservative physical-pixel fit and a generation-matched native text/layout acknowledgement before stable wrapping resumes.

See [MIGRATION.md](./MIGRATION.md) for compatibility details and deferred major-version debt.
28 changes: 28 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Contributing

## Toolchain

- Node 24.15.0
- Corepack 0.36.0 with Yarn 4.5.1
- npm is used only to bootstrap Corepack and inspect the publication tarball;
Yarn remains the sole project dependency resolver

Bootstrap the package root with:

```sh
npm install --global corepack@0.36.0
corepack enable
yarn install --immutable
```

Run the complete non-publishing package gate with:

```sh
yarn release:check
```

That command checks formatting, ESLint, TypeScript, package-owned Jest coverage, the built package, the reviewed API report, exported TSDoc coverage, and the npm tarball manifest. It never updates the API baseline or publishes.

To review an intentional API change, run `yarn prepare` followed by `yarn api:report`, inspect `etc/react-native-awesome-button.api.md`, and update `CHANGELOG.md` plus `MIGRATION.md`. CI uses `yarn api:check` and never rewrites the report.

Tests belong under `src/__tests__`. The Expo demo is a showcase and is not an automated package-test target. Physical-device accessibility, keyboard, RTL, large-text, and Reduced Motion checks remain manual runtime evidence.
42 changes: 42 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Migration Guide

## From 3.1.0 to the engineering-hardening bridge

This pass preserves every 3.1.0 call site while exposing the shared Awesome Button vocabulary. It does not silently enable a new geometry mode.

| Existing API | Bridge API | Current behavior | Future major intent |
| ------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| Outer `style` | `containerStyle` | Both remain valid; `containerStyle` wins conflicts. | Rename the typed visual surface to `style` after removing the outer-style collision. |
| `activityColor` | `buttonStyle.activityColor` | The typed field wins. | Remove the top-level alias. |
| `activeOpacity` | `buttonStyle.activeOpacity` | The typed field wins. | Remove the top-level alias. |
| `backgroundActive` | `buttonStyle.backgroundActive` | The typed field wins. | Remove the top-level alias. |
| `backgroundColor` | `buttonStyle.backgroundColor` | The typed field wins. | Remove the top-level alias. |
| `backgroundPlaceholder` | `buttonStyle.backgroundPlaceholder` | The typed field wins. | Remove the top-level alias. |
| `backgroundProgress` | `buttonStyle.backgroundProgress` | The typed field wins. | Remove the top-level alias. |
| `borderColor` | `buttonStyle.borderColor` | The typed field wins. | Remove the top-level alias. |
| `borderRadius` | `buttonStyle.borderRadius` | The typed field wins. | Remove the top-level alias. |
| `borderTopLeftRadius` | `buttonStyle.borderTopLeftRadius` | The typed field wins. | Remove the top-level alias. |
| `borderTopRightRadius` | `buttonStyle.borderTopRightRadius` | The typed field wins. | Remove the top-level alias. |
| `borderBottomLeftRadius` | `buttonStyle.borderBottomLeftRadius` | The typed field wins. | Remove the top-level alias. |
| `borderBottomRightRadius` | `buttonStyle.borderBottomRightRadius` | The typed field wins. | Remove the top-level alias. |
| `borderWidth` | `buttonStyle.borderWidth` | The typed field wins. | Remove the top-level alias. |
| `paddingHorizontal` | `buttonStyle.paddingHorizontal` | The typed field wins. | Remove the top-level alias. |
| `paddingTop` | `buttonStyle.paddingTop` | The typed field wins. | Remove the top-level alias. |
| `paddingBottom` | `buttonStyle.paddingBottom` | The typed field wins. | Remove the top-level alias. |
| `textFontFamily` | `buttonStyle.textFontFamily` | The typed field wins. | Remove the top-level alias. |
| `textLineHeight` | `buttonStyle.textLineHeight` | The typed field wins. | Remove the top-level alias. |
| `textSize` | `buttonStyle.textSize` | The typed field wins. | Remove the top-level alias. |
| Stack-oriented `height` | `faceHeight` | `faceHeight` wins while `height` retains 3.1.0 geometry. | Make `height` mean face height. |
| `width="auto"` | `autoWidth` on `ThemedButton` | An explicit `autoWidth` decision wins. | Keep one canonical sizing vocabulary. |
| Eventful `onLongPress` | `onLongPressAction` | The eventless action wins and supports assistive activation; the legacy callback remains physical-only. | Make the eventless action canonical. |
| `raiseLevel` | `buttonStyle.raiseAmount` | The typed field wins. | Remove `raiseLevel`. |
| `borderLeftBottomRadius` | `buttonStyle.borderBottomLeftRadius` | The correctly named typed field wins. | Remove the misspelled alias. |
| `backgroundDarker` | `buttonStyle.depthColor` | The typed field wins. | Remove the legacy alias. |
| `backgroundShadow` | `buttonStyle.shadowColor` | The typed field wins. | Remove the legacy alias. |
| `textColor` | `buttonStyle.foregroundColor` | The typed field wins. | Remove the legacy alias. |
| `springRelease={false}` | Native spring release | The legacy non-spring path remains callable. | Use the native spring unconditionally. |
| `twitter` | `x` | `twitter` remains callable; `AwesomeButtonVariant` accepts `x`. | Review removal only in a separately authorized major. |

React Native still retains its 3.1.0 direct geometry defaults and 100 ms press fallback. Other governed ports use the neutral 140 ms fallback. This remains explicit cross-platform debt rather than an implicit behavior change.

No package version or release is selected by this document. Review the checked-in API report and `CHANGELOG.md` before adopting the bridge.
17 changes: 17 additions & 0 deletions PERFORMANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Performance Evidence

Pass 5 treats performance evidence as informational. It does not establish a release threshold.

The package-owned benchmark records React Profiler commit counts for a mounted button receiving rapid A → B → C callback, style, content, and size updates. It also verifies that unmount cleanup prevents post-removal progress and animation dispatch. Run it with:

```sh
yarn test performanceBaseline --runInBand
```

## Pass 5 local baseline

Recorded on 2026-08-30 using a 14-core Apple M3 Max MacBook Pro with 36 GB memory, macOS 26.6.2 (25G83), arm64, Node 24.15.0, Yarn 4.5.1, React 18.2.0, and React Native 0.76.0. Jest ran serially in its package test/development transform environment; this is not an optimized application build or a device frame-rate measurement.

After five unrecorded warm-up repetitions, 25 measured repetitions each mounted one core button, committed three rapid callback/style/content/height replacements, and unmounted it. React Profiler reported exactly eight commits per repetition. The wall-clock median was 2.679 ms with a median absolute deviation of 0.206 ms. Wall-clock values are retained only as local comparison evidence and are never asserted by CI. A second regression verifies that teardown cancels queued progress completion.

A future performance change must retain the observable-behavior tests, rerun the same method on a declared host, and include before/after evidence. No README performance claim is authorized from this initial baseline.
Loading
Loading