chartjs/chartjs-adapter-luxon

Import issue with ChartJS 3.8

winsmith opened this issue · 6 comments

After updating from ChartJS 3.7.1 to ChartJS 3.8, any charts that would use the Luxon adapter stopped working. It seems that ChartJS changed the way their modules are exported slightly and the patches that chartjs-adapter-luxon is making don't register any more, at least in my project.

Any charts that use datetimes don't display any more and the console shows this error message:

Uncaught (in promise) Error: This method is not implemented: Check that a complete date adapter is provided.
    at abstract (chart.esm.js:56:62367)
    at DateAdapter.formats (chart.esm.js:56:62543)
    at TimeScale.init (chart.esm.js:56:280572)
    at eval (chart.esm.js:56:148422)
    at each (helpers.segment.js:129:2545)
    at Chart.buildOrUpdateScales (chart.esm.js:56:147793)
    at Chart._updateScales (chart.esm.js:56:152142)
    at Chart.update (chart.esm.js:56:150911)
    at new Chart (chart.esm.js:56:145260)
    at ChartsPremadeAppVersionsComponent.render (app-versions.js:180:1)

My workaround for this right now has been to create a new helper file, chart-js.js, and doing the patching manually by copy/pasting the code from the adapter source. Then I can import the new Chart object from that helper file.

I realise this is probably very hacky, but I don't really have the experience to find out what the actual problem is.

Here's my new import:

import { Chart } from 'ui/utils/chart-js';

And here's my ui/utils/chart-js.js:

import { Chart, registerables, _adapters } from 'chart.js';

import { DateTime } from 'luxon';

const FORMATS = {
  datetime: DateTime.DATETIME_MED_WITH_SECONDS,
  millisecond: 'h:mm:ss.SSS a',
  second: DateTime.TIME_WITH_SECONDS,
  minute: DateTime.TIME_SIMPLE,
  hour: { hour: 'numeric' },
  day: { day: 'numeric', month: 'short' },
  week: 'DD',
  month: { month: 'short', year: 'numeric' },
  quarter: "'Q'q - yyyy",
  year: { year: 'numeric' },
};

_adapters._date.override({
  _id: 'luxon', // DEBUG

  /**
   * @private
   */
  _create: function (time) {
    return DateTime.fromMillis(time, this.options);
  },

  formats: function () {
    return FORMATS;
  },

  parse: function (value, format) {
    const options = this.options;

    if (value === null || typeof value === 'undefined') {
      return null;
    }

    const type = typeof value;
    if (type === 'number') {
      value = this._create(value);
    } else if (type === 'string') {
      if (typeof format === 'string') {
        value = DateTime.fromFormat(value, format, options);
      } else {
        value = DateTime.fromISO(value, options);
      }
    } else if (value instanceof Date) {
      value = DateTime.fromJSDate(value, options);
    } else if (type === 'object' && !(value instanceof DateTime)) {
      value = DateTime.fromObject(value);
    }

    return value.isValid ? value.valueOf() : null;
  },

  format: function (time, format) {
    const datetime = this._create(time);
    return typeof format === 'string'
      ? datetime.toFormat(format, this.options)
      : datetime.toLocaleString(format);
  },

  add: function (time, amount, unit) {
    const args = {};
    args[unit] = amount;
    return this._create(time).plus(args).valueOf();
  },

  diff: function (max, min, unit) {
    return this._create(max).diff(this._create(min)).as(unit).valueOf();
  },

  startOf: function (time, unit, weekday) {
    if (unit === 'isoWeek') {
      weekday = Math.trunc(Math.min(Math.max(0, weekday), 6));
      const dateTime = this._create(time);
      return dateTime
        .minus({ days: (dateTime.weekday - weekday + 7) % 7 })
        .startOf('day')
        .valueOf();
    }
    return unit ? this._create(time).startOf(unit).valueOf() : time;
  },

  endOf: function (time, unit) {
    return this._create(time).endOf(unit).valueOf();
  },
});

Chart.register(...registerables);

export { Chart };

@winsmith may I ask you if you can reproduce the issue in codepen?

Only thing I can think off by that error is that you dont import the adapter, thats the only way you get that error afaik, when I try it, it seems to work fine: https://codesandbox.io/s/react-typescript-forked-5s45fs?file=/src/App.tsx

Is there a reason ChartJS doesn't support native Javascript dates, rather than forcing an adapter ?

Date has basically no features. Hopefully we can remove date adapters after https://github.com/tc39/proposal-temporal

And I assume you need some of these features with ChartJS ? May I ask why, if you also support raw timestamps (that have no features since they are just numbers)

You can take a look here for everything we need to do:
https://github.com/chartjs/chartjs-adapter-luxon/blob/master/src/index.js