From 8dadee0376f71e900a24048cb4f5a7315222869e Mon Sep 17 00:00:00 2001 From: mahmoud adel <58145645+mahmoudadel54@users.noreply.github.com> Date: Mon, 11 Sep 2023 15:54:32 +0300 Subject: [PATCH] #9275 Fix issue Invalid date on Firefox (#9393) --- .../formatters/__tests__/index-test.jsx | 184 ++++++++++++------ .../data/featuregrid/formatters/index.js | 5 +- 2 files changed, 126 insertions(+), 63 deletions(-) diff --git a/web/client/components/data/featuregrid/formatters/__tests__/index-test.jsx b/web/client/components/data/featuregrid/formatters/__tests__/index-test.jsx index f65164baf9..5e0a27287e 100644 --- a/web/client/components/data/featuregrid/formatters/__tests__/index-test.jsx +++ b/web/client/components/data/featuregrid/formatters/__tests__/index-test.jsx @@ -5,118 +5,178 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import expect from 'expect'; +import React from "react"; +import ReactDOM from "react-dom"; +import expect from "expect"; -import NumberFormat from '../../../../I18N/Number'; -import {getFormatter, registerFormatter, unregisterFormatter} from '../index'; +import NumberFormat from "../../../../I18N/Number"; +import { getFormatter, registerFormatter, unregisterFormatter } from "../index"; -describe('Tests for the formatter functions', () => { - it('test getFormatter for booleans', () => { - const formatter = getFormatter({localType: "boolean"}); +describe("Tests for the formatter functions", () => { + it("test getFormatter for booleans", () => { + const formatter = getFormatter({ localType: "boolean" }); expect(typeof formatter).toBe("function"); expect(formatter()).toBe(null); - expect(formatter({value: true}).type).toBe("span"); - expect(formatter({value: true}).props.children).toBe("true"); - expect(formatter({value: false}).props.children).toBe("false"); - expect(formatter({value: null})).toBe(null); - expect(formatter({value: undefined})).toBe(null); + expect(formatter({ value: true }).type).toBe("span"); + expect(formatter({ value: true }).props.children).toBe("true"); + expect(formatter({ value: false }).props.children).toBe("false"); + expect(formatter({ value: null })).toBe(null); + expect(formatter({ value: undefined })).toBe(null); }); - it('test getFormatter for strings', () => { - const value = 'Test https://google.com with google link'; - const formatter = getFormatter({localType: "string"}); + it("test getFormatter for strings", () => { + const value = "Test https://google.com with google link"; + const formatter = getFormatter({ localType: "string" }); expect(typeof formatter).toBe("function"); expect(formatter()).toBe(null); - expect(formatter({value: 'Test no links'})[0]).toBe('Test no links'); - expect(formatter({value})[0]).toBe('Test '); - expect(formatter({value})[1].props.href).toBe('https://google.com'); - expect(formatter({value})[2]).toBe(' with google link'); - expect(formatter({value: null})).toBe(null); - expect(formatter({value: undefined})).toBe(null); + expect(formatter({ value: "Test no links" })[0]).toBe("Test no links"); + expect(formatter({ value })[0]).toBe("Test "); + expect(formatter({ value })[1].props.href).toBe("https://google.com"); + expect(formatter({ value })[2]).toBe(" with google link"); + expect(formatter({ value: null })).toBe(null); + expect(formatter({ value: undefined })).toBe(null); }); - it('test getFormatter for number', () => { - const formatter = getFormatter({localType: "number"}); + it("test getFormatter for number", () => { + const formatter = getFormatter({ localType: "number" }); expect(typeof formatter).toBe("function"); expect(formatter()).toBe(null); - expect(formatter({value: 44.3333434353535}).type).toBe(NumberFormat); - expect(formatter({value: 44.3333434353535}).props.value).toBe(44.3333434353535); - expect(formatter({value: null})).toBe(null); - expect(formatter({value: undefined})).toBe(null); - expect(formatter({value: 0}).props.value).toBe(0); + expect(formatter({ value: 44.3333434353535 }).type).toBe(NumberFormat); + expect(formatter({ value: 44.3333434353535 }).props.value).toBe( + 44.3333434353535 + ); + expect(formatter({ value: null })).toBe(null); + expect(formatter({ value: undefined })).toBe(null); + expect(formatter({ value: 0 }).props.value).toBe(0); }); - it('test getFormatter for int', () => { - const formatter = getFormatter({localType: "int"}); + it("test getFormatter for int", () => { + const formatter = getFormatter({ localType: "int" }); expect(typeof formatter).toBe("function"); expect(formatter()).toBe(null); - expect(formatter({value: 2455567}).type).toBe(NumberFormat); - expect(formatter({value: 2455567}).props.value).toBe(2455567); - expect(formatter({value: null})).toBe(null); - expect(formatter({value: undefined})).toBe(null); - expect(formatter({value: 0}).props.value).toBe(0); + expect(formatter({ value: 2455567 }).type).toBe(NumberFormat); + expect(formatter({ value: 2455567 }).props.value).toBe(2455567); + expect(formatter({ value: null })).toBe(null); + expect(formatter({ value: undefined })).toBe(null); + expect(formatter({ value: 0 }).props.value).toBe(0); }); - it('test getFormatter for geometry', () => { - const formatter = getFormatter({localType: "Geometry"}); + it("test getFormatter for geometry", () => { + const formatter = getFormatter({ localType: "Geometry" }); expect(typeof formatter).toBe("function"); expect(formatter()).toBe(null); - expect(formatter({value: {properties: {}, geometry: {type: "Point", coordinates: [1, 2]}}})).toBe(null); - expect(formatter({value: null})).toBe(null); - expect(formatter({value: undefined})).toBe(null); + expect( + formatter({ + value: { + properties: {}, + geometry: { type: "Point", coordinates: [1, 2] } + } + }) + ).toBe(null); + expect(formatter({ value: null })).toBe(null); + expect(formatter({ value: undefined })).toBe(null); }); - describe('test featureGridFormatter', () => { + describe("test featureGridFormatter", () => { beforeEach((done) => { document.body.innerHTML = '
'; setTimeout(done); }); afterEach((done) => { - ReactDOM.unmountComponentAtNode(document.getElementById("container")); - document.body.innerHTML = ''; + ReactDOM.unmountComponentAtNode( + document.getElementById("container") + ); + document.body.innerHTML = ""; setTimeout(done); }); - it('base', () => { + it("base", () => { try { - registerFormatter("test", ({config, value}) => { + registerFormatter("test", ({ config, value }) => { expect(config).toExist(); expect(value).toBe("test"); return
test
; }); - const Formatter = getFormatter({localType: "test"}, {featureGridFormatter: {name: "test"}}); - ReactDOM.render(, document.getElementById("container")); - expect(document.getElementById("container").innerHTML).toBe('
test
'); + const Formatter = getFormatter( + { localType: "test" }, + { featureGridFormatter: { name: "test" } } + ); + ReactDOM.render( + , + document.getElementById("container") + ); + expect(document.getElementById("container").innerHTML).toBe( + "
test
" + ); } finally { unregisterFormatter("test"); } }); - it('with directRender option', () => { + it("with directRender option", () => { try { const TEST_FUNC = () =>
test
; registerFormatter("test", TEST_FUNC); - const formatter = getFormatter({localType: "test"}, {featureGridFormatter: {name: "test", directRender: true}}); + const formatter = getFormatter( + { localType: "test" }, + { + featureGridFormatter: { + name: "test", + directRender: true + } + } + ); expect(formatter).toBe(TEST_FUNC); } finally { unregisterFormatter("test"); } }); }); - it('test getFormatter for date / date-time / time', () => { + it("test getFormatter for date / date-time / time", () => { const dateFormats = { - date: 'YYYY', - "date-time": 'YYYY DD', - time: 'HH:mm' + date: "YYYY", + "date-time": "YYYY DD", + time: "HH:mm" }; - const dateFormatter = getFormatter({localType: "date"}, undefined, {dateFormats}); - const dateTimeFormatter = getFormatter({localType: "date-time"}, undefined, {dateFormats}); - const timeFormatter = getFormatter({localType: "time"}, undefined, {dateFormats}); + const dateFormatter = getFormatter({ localType: "date" }, undefined, { + dateFormats + }); + const dateTimeFormatter = getFormatter( + { localType: "date-time" }, + undefined, + { dateFormats } + ); + const timeFormatter = getFormatter({ localType: "time" }, undefined, { + dateFormats + }); expect(typeof dateFormatter).toBe("function"); expect(dateFormatter()).toBe(null); - expect(dateFormatter({value: '2015-02-01T12:45:00Z'})).toBe('2015'); + expect(dateFormatter({ value: "2015-02-01T12:45:00Z" })).toBe("2015"); expect(typeof dateTimeFormatter).toBe("function"); expect(dateTimeFormatter()).toBe(null); - expect(dateTimeFormatter({value: '2015-02-01Z'})).toBe('2015 01'); + expect(dateTimeFormatter({ value: "2015-02-01Z" })).toBe("2015 01"); expect(typeof timeFormatter).toBe("function"); expect(timeFormatter()).toBe(null); - expect(timeFormatter({value: '12:45:00Z'})).toBe('12:45'); - expect(timeFormatter({ value: '1970-01-01T02:30:00Z' })).toBe('02:30'); // still able to format time even when found a full date (sometimes GeoServer returns full date instead of time only) + expect(timeFormatter({ value: "12:45:00Z" })).toBe("12:45"); + expect(timeFormatter({ value: "1970-01-01T02:30:00Z" })).toBe("02:30"); // still able to format time even when found a full date (sometimes GeoServer returns full date instead of time only) + }); + + it("test getFormatter for invalid date-time YYYY-MM-DD[Z]", () => { + const dateFormats = { + "date-time": "YYYY-MM-DD[Z]" + }; + const dateTimeWithZFormatter = getFormatter( + { localType: "date-time" }, + undefined, + { dateFormats } + ); + expect(typeof dateTimeWithZFormatter).toBe("function"); + expect(dateTimeWithZFormatter({ value: "2015-02-01Z" })).toBe( + "2015-02-01Z" + ); + expect(dateTimeWithZFormatter({ value: "2015-02-01" })).toBe( + "2015-02-01Z" + ); + expect(dateTimeWithZFormatter({ value: "2015/02/01" })).toBe( + "2015-02-01Z" + ); + expect(dateTimeWithZFormatter({ value: "2015/02/01 03:20:10" })).toBe( + "2015-02-01Z" + ); + expect(dateTimeWithZFormatter({ value: "2015-02-01T12:45:00Z"})).toBe('2015-02-01Z'); }); }); diff --git a/web/client/components/data/featuregrid/formatters/index.js b/web/client/components/data/featuregrid/formatters/index.js index 45979a4ab2..28f2d339a6 100644 --- a/web/client/components/data/featuregrid/formatters/index.js +++ b/web/client/components/data/featuregrid/formatters/index.js @@ -20,13 +20,16 @@ const StringFormatter = ({value} = {}) => !isNil(value) ? reactStringReplace(val )) : null; const NumberFormatter = ({value} = {}) => !isNil(value) ? : null; const DEFAULT_DATE_PART = "1970-01-01"; +const DATE_INPUT_FORAMAT = "YYYY-MM-DD[Z]"; const dateTimeFormatter = ({value, format, type}) => { return !isNil(value) ? moment.utc(value).isValid() // geoserver sometimes returns UTC for time. ? moment.utc(value).format(format) : type === 'time' ? moment(`${DEFAULT_DATE_PART}T${value}`).utc().format(format) // time format append default date part - : moment(value).format(format) // date or date-time formats + : type === "date" && value?.toLowerCase()?.endsWith("z") // in case: date format and value ends with z + ? moment(value, DATE_INPUT_FORAMAT).format(format) + : moment(value).format(format) : null; }; export const register = {};