Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
^node_modules$
^package\.json$
^package-lock\.json$
^examples$
189 changes: 189 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# gglite - Repository Instructions for Copilot

## Repository Overview

This is an R package that provides a lightweight interface to the AntV G2
JavaScript visualization library with a ggplot2-style API. It supports
rendering in R Markdown, litedown, Shiny, and standalone HTML previews.

**Project Type**: R package
**Languages**: R, JavaScript (via CDN)
**Size**: Small repository (~15 R source files)
**Target Runtime**: R (>= 3.5.0)

## Build and Test Instructions

### Prerequisites

**Note**: R is automatically installed via `.github/copilot-setup-steps.yml`
when working with GitHub Copilot. For manual setup:
- R (>= 3.5.0) - available via `copilot-setup-steps.yml`
- R package dependencies (testit, roxygen2, xfun) - available via
`copilot-setup-steps.yml`

**For testing on Linux manually**: If not using Copilot's automated setup,
install the latest R from CRAN, not from Debian/Ubuntu official repositories.
Follow instructions at https://cran.r-project.org/bin/linux/ubuntu to ensure
you're testing with the most recent R version that users will have.

### Bootstrap and Build Sequence

1. **Build the R package**:
```bash
R CMD build .
```

2. **Install the package**:
```bash
R CMD INSTALL gglite_*.tar.gz
```

3. **Run tests**:
```bash
R CMD check gglite_*.tar.gz --no-manual
```
or directly:
```bash
Rscript tests/test-all.R
```
- Tests use the `testit` package
- All tests should pass without errors

### Testing Conventions

- Tests are in `tests/testit/test-gglite.R`
- Use `testit` package for assertions
- Always wrap test conditions in `{}`: `assert('message', {})`
- Use `has_error()` instead of `tryCatch()` for error testing
- Load the package with `library(gglite)` before testing

### Testing Plots in Headless Browsers

Since gglite generates HTML/JavaScript visualizations, **plots must be tested
in headless browsers** to make sure they can be rendered correctly and produce
no errors in the browser console. Use tools such as Puppeteer or Playwright to
open the generated HTML and verify that:

1. The chart container element exists in the DOM.
2. The G2 chart renders without JavaScript errors.
3. No warnings or errors appear in the browser console.

## Project Structure

### Key Files and Directories

**Root level**:
- `DESCRIPTION` - R package metadata
- `NAMESPACE` - R package namespace (auto-generated by roxygen2)
- `NEWS.md` - Changelog
- `README.md` - Package documentation
- `examples/` - Rmd example files for each chart component

**R code** (`R/`):
- `gglite.R` - Core: package doc, CDN URLs, `g2()`, `encode()`,
`annotate_df()`
- `mark.R` - All 35 mark (geometry) functions
- `scale.R` - `scale_of()`
- `coordinate.R` - `coordinate()`, `coord_transpose()`
- `interact.R` - `interact()`
- `theme.R` - `theme_of()`
- `transform.R` - `transform_of()`
- `facet.R` - `facet_rect()`, `facet_circle()`
- `animate.R` - `animate()`
- `component.R` - `axis_of()`, `legend_of()`, `title_of()`, `tooltip_of()`,
`labels_of()`, `style_mark()`, `slider_of()`, `scrollbar_of()`
- `render.R` - `build_config()`, `chart_html()`, `preview()`, `print.g2()`,
`knit_print.g2()`, `record_print.g2()`, `render_shiny()`

**Tests** (`tests/`):
- `test-all.R` - Entry point
- `testit/test-gglite.R` - All package tests using testit framework

### CI/CD Configuration

**GitHub Actions** (`.github/workflows/`):
- `R-CMD-check.yaml` - Runs R CMD check on multiple platforms
- `copilot-setup-steps.yml` - Sets up the environment for Copilot
- `github-pages.yml` - Builds and deploys the package site via litedown

## Validation Steps

Before submitting changes:

1. Run `R CMD build .` to build the package
2. Run `R CMD check gglite_*.tar.gz --no-manual` to validate
3. Ensure all tests pass: `Rscript tests/test-all.R`
4. Check GitHub Actions status for multi-platform validation
5. Update `NEWS.md` to document your changes

## Important Conventions

### R Code Style

1. **Assignment**: Use `=` instead of `<-` for assignment
2. **Strings**: Use single quotes for strings (e.g., `'text'`)
3. **Indentation**: Use 2 spaces (not 4 spaces or tabs)
4. **Compact code**: Avoid `{}` for single-expression if statements; prefer
compact forms when possible
5. **Roxygen documentation**: Don't use `@description` or `@details`
explicitly — just write the description text directly after the title. Don't
use `@title` either.
6. **Examples**: Avoid `\dontrun{}` unless absolutely necessary. Prefer
runnable examples that can be tested automatically.
7. **Function definitions**: For functions with many arguments, break the line
right after the opening `(`, indent arguments by 2 spaces, and try to wrap
them at 80-char width.
8. **Re-wrap code**: Always re-wrap the code after making changes to maintain
consistent formatting and line length.
9. **JavaScript in R**: Use `const` and arrow functions (`=>`) in JS,
`type="module"` for inline scripts, `defer` for external scripts.

### Variables Are Character Strings

gglite does **NOT** use non-standard evaluation (NSE). Variables are specified
as character strings, e.g., `g2(mtcars, x = 'mpg', y = 'hp')`.

### Testing Conventions

1. **Use testit properly**: Write all test conditions in `()`, use `%==%` to
test for `identical()`, and test conditions can return vectors.
```r
assert("test description", {
(length(result) %==% 3L)
(file.exists(result))
})
```

### Build and Package Conventions

1. **Always re-roxygenize**: Run `roxygen2::roxygenize()` after changing any
roxygen documentation to update man files
2. **MANDATORY: R CMD check before EVERY commit**: You MUST run `R CMD check`
successfully before submitting ANY code changes.
3. **MANDATORY: Wait for CI to be green**: After pushing code, you MUST wait
for GitHub Actions CI to complete successfully before claiming the task is
done.
4. **Bump version in PRs**: Bump the patch version number in DESCRIPTION once
per PR (on the first commit or when you first make changes), not on every
commit to the PR
5. **NEVER BREAK CI**: Breaking CI is completely unacceptable. If CI fails, you
must immediately fix it.
6. **Never commit binary files**: Avoid version-controlling binary files,
especially automatically generated ones.
7. **Testing**: Use testit assertions with proper error handling
8. **Update NEWS.md**: When making changes, make sure to update `NEWS.md`
accordingly to document what changed.

## Package API

The main entry point is `g2()` which creates a chart object, then pipe
operators (`|>`) chain mark, scale, coordinate, interaction, theme, and
component functions:

```r
g2(mtcars, x = 'mpg', y = 'hp') |>
mark_point() |>
scale_of('x', type = 'log') |>
theme_of('dark') |>
title_of('Motor Trend Cars')
```
26 changes: 26 additions & 0 deletions .github/workflows/copilot-setup-steps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Copilot Setup Steps

on:
workflow_dispatch:
push:
paths:
- .github/workflows/copilot-setup-steps.yml
pull_request:
paths:
- .github/workflows/copilot-setup-steps.yml

jobs:
copilot-setup-steps:
runs-on: ubuntu-latest

permissions:
contents: read

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Install R
uses: r-lib/actions/setup-r@HEAD
with:
use-public-rspm: true
40 changes: 40 additions & 0 deletions .github/workflows/github-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Build and deploy package site

on:
push:
branches: ["main"]

permissions:
contents: read
pages: write
id-token: write

jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/configure-pages@v5
with:
enablement: true

- uses: r-lib/actions/setup-r@HEAD
with:
use-public-rspm: true

- uses: r-lib/actions/setup-r-dependencies@HEAD

- uses: yihui/litedown/site@HEAD
with:
site-dir: 'site'

- uses: actions/upload-pages-artifact@v3
with:
path: 'site'

- id: deployment
uses: actions/deploy-pages@v4
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Generated by roxygen2: do not edit by hand

S3method(print,g2)
S3method(record_print,g2)
export(animate)
export(axis_of)
export(chart_html)
Expand Down
23 changes: 23 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# gglite 0.0.1
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the first heading in NEWS.md always represents the dev version and it should be of the form # PKG x.y where PKG is the package name, and x.y is the next version to be released to CRAN (note it's x.y, not x.y.0); usually y is bumped from the current minor version, e.g., if the current dev version is 1.8.3, the next CRAN release is expected to be 1.9

revise the version number and add this to copilot instructions accordingly

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a1d78a8. NEWS.md heading now uses # gglite 0.1 format. Added the convention to .github/copilot-instructions.md under Build and Package Conventions item 8.


- Changed `g2_col_cdn()` from a function to a constant `g2_col_cdn` since the
CDN URL is unlikely to change.

- Refactored the CDN `<script>` tags in `preview()` and `knit_print.g2()` into
a shared helper `cdn_scripts()` to reduce code repetition.

- Added `record_print.g2()` S3 method for `xfun::record_print()` so that
litedown can render gglite plots correctly in Rmd documents.

- Added `.github/copilot-instructions.md` for Copilot development guidance.

- Added `.github/workflows/copilot-setup-steps.yml` for Copilot environment
setup.

- Added `.github/workflows/github-pages.yml` for building the package site with
litedown.

- Added `examples/` directory with extensive Rmd examples for each gglite
component (marks, scales, coordinates, themes, transforms, interactions,
facets, animations, axes, legends, titles, tooltips, labels, styles, sliders,
and scrollbars).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't mention trivial technical details that are worthless to end users

for v0.1, just provide a summary of what this package does since everything is new

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a1d78a8. NEWS.md now just summarizes what the package does for v0.1.

7 changes: 1 addition & 6 deletions R/gglite.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,7 @@ g2_cdn = function() {
getOption('gglite.g2_cdn', 'https://unpkg.com/@antv/g2@5/dist/g2.min.js')
}

#' CDN URL for the G2 Column-Major Data Helper
#' @return A character string.
#' @keywords internal
g2_col_cdn = function() {
'https://cdn.jsdelivr.net/npm/@xiee/utils/js/g2-column.min.js'
}
g2_col_cdn = 'https://cdn.jsdelivr.net/npm/@xiee/utils/js/g2-column.min.js'

#' Create a G2 Chart Object
#'
Expand Down
25 changes: 17 additions & 8 deletions R/render.R
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ chart_html = function(chart, id = NULL, width = NULL, height = NULL) {
)
}

cdn_scripts = function() {
paste0(
'<script defer src="', g2_cdn(), '"></script>\n',
'<script defer src="', g2_col_cdn, '"></script>'
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put defer at the end:

Suggested change
'<script defer src="', g2_cdn(), '"></script>\n',
'<script defer src="', g2_col_cdn, '"></script>'
'<script src="', g2_cdn(), '" defer></script>\n',
'<script src="', g2_col_cdn, ' defer"></script>'

actually you should avoid repetition by a single sprintf() call that accepts a char vector of both g2 cdn and g2 col cdn

and there's no need to concatenate HTML by \n: both litedown and xfun supports char vectors; you only need to join by \n for knit_print.g2(); use natural char vectors elsewhere and don't join by \n

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a1d78a8. cdn_scripts() now uses sprintf('<script src="%s" defer></script>', c(g2_cdn(), g2_col_cdn)) returning a char vector. preview() and record_print.g2() use natural char vectors; only knit_print.g2() joins by \n.

)
}

#' Preview a Chart in the Viewer or Browser
#'
#' @param chart A `g2` object.
Expand All @@ -99,8 +106,7 @@ preview = function(chart, ...) {
html = paste0(
'<!DOCTYPE html>\n<html>\n<head>\n',
'<meta charset="utf-8">\n',
'<script defer src="', g2_cdn(), '"></script>\n',
'<script defer src="', g2_col_cdn(), '"></script>\n',
cdn_scripts(), '\n',
'</head>\n<body>\n',
body, '\n',
'</body>\n</html>'
Expand All @@ -125,20 +131,23 @@ print.g2 = function(x, ...) {
#' @param ... Ignored.
#' @return A `knit_asis` character vector.
knit_print.g2 = function(x, ...) {
cdn = paste0(
'<script defer src="', g2_cdn(), '"></script>\n',
'<script defer src="', g2_col_cdn(), '"></script>'
)
body = chart_html(x, ...)
out = paste0(cdn, '\n', body)
out = paste0(cdn_scripts(), '\n', chart_html(x, ...))
structure(out, class = c('knit_asis', 'html'))
}

#' @export
record_print.g2 = function(x, ...) {
out = paste0(cdn_scripts(), '\n', chart_html(x, ...))
xfun:::new_record(out, 'asis')
}

.onLoad = function(libname, pkgname) {
if (requireNamespace('knitr', quietly = TRUE)) {
registerS3method('knit_print', 'g2', knit_print.g2,
envir = asNamespace('knitr'))
}
registerS3method('record_print', 'g2', record_print.g2,
envir = asNamespace('xfun'))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not necessary; method already exported

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already removed in 540ec20 (previous commit). .onLoad() now only registers knit_print.g2 (since knitr is in Suggests, not Imports).

}

#' Render a Chart in Shiny
Expand Down
Loading
Loading