Skip to content

feat(playground): Use format API from client-core#10693

Open
ovr wants to merge 1 commit intomasterfrom
feat/playground-client-core-format-api
Open

feat(playground): Use format API from client-core#10693
ovr wants to merge 1 commit intomasterfrom
feat/playground-client-core-format-api

Conversation

@ovr
Copy link
Copy Markdown
Member

@ovr ovr commented Apr 15, 2026

No description provided.

@ovr ovr requested a review from a team as a code owner April 15, 2026 14:27
@ovr ovr requested review from keydunov and vasilev-alex April 15, 2026 14:27
@github-actions github-actions bot added client:core Issues relating to the JavaScript client SDK client:playground Issues relating to the Developer Playground javascript Pull requests that update Javascript code labels Apr 15, 2026
@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 15, 2026

Claude finished @ovr's task in 3m 25s —— View job


Code Review: feat(playground): Use format API from client-core

  • Gather context and read the PR diff
  • Review client-core format changes
  • Review playground integration changes
  • Review test coverage
  • Post comprehensive review feedback

Overall: Looks good. This is a well-structured consolidation that moves formatting logic out of the playground and into @cubejs-client/core/format, reducing duplication across 3+ call sites. Net −34 lines with better test coverage — a clean win.

What I Like

  • Single source of truthformatValue and formatDateByGranularity in client-core now serve both the playground and any other consumer, eliminating duplicated formatting logic in QueryBuilderResults, ChartRenderer, TableQueryRenderer, and the old format-date-by-granularity.tsx utility.
  • Leaner locale definitions — Replacing 13 full JSON imports from d3-format/locale/*.json with inline { decimal, thousands, grouping } objects reduces bundle size and makes it clear that only number formatting (not currency) is pre-built. The type change to Omit<FormatLocaleDefinition, 'currency'> is accurate.
  • Fixed fallback currency — The catch block in getD3NumericLocale (line 97–103) now correctly applies getCurrencyOverride instead of using the bare en-US definition without currency resolution. This was a subtle bug fix.
  • Better granularity formats — The new patterns (%Y %b for month, %Y-%m-%d W%V for week, %Y-%m-%d %H:00 for hour, etc.) are more informative and human-readable than the previous defaults.
  • Good test coverage — The new formatDateByGranularity test suite covers all 8 granularities, multiple input types (Date, ISO string, epoch number), fallback for unknown granularities, and invalid date handling.
  • Flexible input typesformatDateByGranularity now accepts Date | string | number instead of only Date, which is more ergonomic for callers.

Issues & Suggestions

  1. ChartRenderer render path asymmetry (non-blocking) — In ChartRenderer.tsx, the granularity branch (lines 445–457, unchanged) still uses formatDateByGranularity directly with isValidISOTimestamp + try/catch, while the non-granularity branch (lines 458–469) uses formatValue. Since formatValue({ type: 'time' }) internally delegates to formatDateByGranularity and handles invalid dates, these could be unified. The granularity branch also doesn't handle null/undefined the way the non-granularity branch does. See inline comment for details.

  2. Misleading filename (non-blocking) — format-date-by-granularity.tsx no longer exports formatDateByGranularity — it only has formatDateByPattern. Consider renaming to format-date-by-pattern.tsx to match its content.

  3. User-visible format changes (informational) — Several time format patterns changed (month: 2024-032024 Mar, hour: 2024-03-15 14:00:002024-03-15 14:00, week gains W11 suffix, quarter: 2024-Q12024 Q1). Tests are updated accordingly. These are improvements, just flagging since any existing consumer of formatValue with time types will see different output.

Security & Performance

  • No security concerns — no user input is passed to shell commands or injected into HTML.
  • The locale cache in getD3NumericLocale prevents repeated Intl/formatLocale work — no performance regressions.
  • Removing 13 JSON imports from d3-format is a small but positive bundle size improvement.

Comment on lines +459 to +468
if (text === null) return <Tag>NULL</Tag>;
if (typeof text === 'object') return <Tag>OBJECT</Tag>;
if (text === undefined) return '';

return formatValue(text, {
type: c.type,
format: c.format,
currency: c.currency,
granularity: c.granularity,
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit (consistency): The non-granularity branch here now uses formatValue nicely, but the granularity branch above (lines 446–457, unchanged in this PR) still uses formatDateByGranularity directly with an isValidISOTimestamp guard and try/catch.

Since formatValue with type: 'time' internally delegates to formatDateByGranularity and already handles invalid dates gracefully (returns "Invalid date"), you could consider unifying both branches into a single render function to reduce the asymmetry. The granularity branch also doesn't handle null/undefined the way this branch does — if text is null, isValidISOTimestamp(null) returns false and the raw null passes through.

Not blocking, but worth considering for a follow-up.

Comment on lines 1 to 5
import formatDate from 'date-fns/format';

const FORMAT_MAP = {
second: 'yyyy-LL-dd HH:mm:ss',
minute: 'yyyy-LL-dd HH:mm',
hour: 'yyyy-LL-dd HH:00',
day: 'yyyy-LL-dd',
week: "yyyy-LL-dd 'W'w",
month: 'yyyy LLL',
quarter: 'yyyy QQQ',
year: 'yyyy',
};

export function formatDateByGranularity(timestamp: Date, granularity?: TimeDimensionGranularity) {
return formatDate(
timestamp,
FORMAT_MAP[(granularity as Exclude<TimeDimensionGranularity, string>) ?? 'second'] ??
FORMAT_MAP['second']
);
}

export function formatDateByPattern(timestamp: Date, format?: string) {
return formatDate(timestamp, format ?? FORMAT_MAP['second']);
return formatDate(timestamp, format ?? 'yyyy-LL-dd HH:mm:ss');
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: Now that formatDateByGranularity has been removed from this file, the filename format-date-by-granularity.tsx no longer accurately describes its content — it only exports formatDateByPattern. Consider renaming to format-date-by-pattern.tsx (and updating the re-export in utils/index.ts) to avoid confusion.

Comment on lines +7 to 21
export const formatD3NumericLocale: Record<string, Omit<FormatLocaleDefinition, 'currency'>> = {
'en-US': { decimal: '.', thousands: ',', grouping: [3] },
'en-GB': { decimal: '.', thousands: ',', grouping: [3] },
'zh-CN': { decimal: '.', thousands: ',', grouping: [3] },
'es-ES': { decimal: ',', thousands: '.', grouping: [3] },
'es-MX': { decimal: '.', thousands: ',', grouping: [3] },
'de-DE': { decimal: ',', thousands: '.', grouping: [3] },
'ja-JP': { decimal: '.', thousands: ',', grouping: [3] },
'fr-FR': { decimal: ',', thousands: '\u00a0', grouping: [3], percent: '\u202f%' },
'pt-BR': { decimal: ',', thousands: '.', grouping: [3] },
'ko-KR': { decimal: '.', thousands: ',', grouping: [3] },
'it-IT': { decimal: ',', thousands: '.', grouping: [3] },
'nl-NL': { decimal: ',', thousands: '.', grouping: [3] },
'ru-RU': { decimal: ',', thousands: '\u00a0', grouping: [3] },
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good change — inlining only the needed properties (decimal, thousands, grouping) instead of importing full JSON locale files from d3-format reduces bundle size and makes the type more accurate with Omit<FormatLocaleDefinition, 'currency'> since currency is always resolved dynamically via getCurrencyOverride.

Comment on lines 97 to 103
console.warn('Failed to generate d3 local via Intl, failing back to en-US', e);

definition = formatD3NumericLocale['en-US'];
definition = {
...formatD3NumericLocale['en-US'],
currency: getCurrencyOverride(locale, currencyCode)
};
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good fix — the fallback path previously used the bare formatD3NumericLocale['en-US'] without currency resolution. Now it properly applies getCurrencyOverride, ensuring currency symbols are correct even in the error fallback.

Comment on lines +28 to +37
const DATETIME_FORMAT_BY_GRANULARITY: Record<string, string> = {
second: '%Y-%m-%d %H:%M:%S',
minute: '%Y-%m-%d %H:%M',
hour: '%Y-%m-%d %H:00',
day: '%Y-%m-%d',
week: '%Y-%m-%d W%V',
month: '%Y %b',
quarter: '%Y Q%q',
year: '%Y',
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The format patterns were changed for several granularities compared to the previous version:

  • minute: %Y-%m-%d %H:%M:%S%Y-%m-%d %H:%M (drops seconds — good)
  • hour: %Y-%m-%d %H:%M:%S%Y-%m-%d %H:00 (shows truncated hour — good)
  • week: %Y-%m-%d%Y-%m-%d W%V (adds ISO week number — more informative)
  • month: %Y-%m%Y %b (abbreviated month name — more readable)
  • quarter: %Y-Q%q%Y Q%q (space instead of dash)

These are user-visible formatting changes and improvements. They match what the playground previously had in its own FORMAT_MAP (date-fns patterns). Just flagging that any consumers of the old formatValue with time types will see different output.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 58.03%. Comparing base (9fee612) to head (a6cd01b).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #10693   +/-   ##
=======================================
  Coverage   58.03%   58.03%           
=======================================
  Files         215      215           
  Lines       16683    16683           
  Branches     3357     3357           
=======================================
  Hits         9682     9682           
  Misses       6511     6511           
  Partials      490      490           
Flag Coverage Δ
cube-backend 58.03% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

client:core Issues relating to the JavaScript client SDK client:playground Issues relating to the Developer Playground javascript Pull requests that update Javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant