Offline Fallback Using Workbox-build - progressive-web-apps

So I use gulp to generate service-worker.js. In the developer console I see all my assets plus an offline page loaded. And yet, when triggering "Offline Mode" the browser doesn't redirect to /offline.
Here is the code I use:
const genRanHex = (size = 24) => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
workbox.generateSW({
cacheId: "ChristianWritings",
globDirectory: "./public",
globPatterns: [
"**/*.{css,js,eot,ttf,woff,woff2,otf}"
],
swDest: "./public/service-worker.js",
modifyURLPrefix: {
"": "/"
},
clientsClaim: true,
skipWaiting: true,
ignoreURLParametersMatching: [/./],
offlineGoogleAnalytics: true,
additionalManifestEntries: [
{
"url": "/offline",
"revision": genRanHex()
}
],
maximumFileSizeToCacheInBytes: 50 * 1024 * 1024,
runtimeCaching: [
urlPattern: /^https:\/\/([\w+\.\-]+www\.mywebsite\.org)(|\/.*)$/,
handler: 'NetworkOnly',
options: {
precacheFallback: {
fallbackURL: '/offline',
},
},
{
urlPattern: /(?:\/)$/,
handler: "StaleWhileRevalidate",
options: {
cacheName: "html",
expiration: {
maxAgeSeconds: 60 * 60 * 24 * 7,
},
},
},
{
urlPattern: /\.(?:png|jpg|jpeg|gif|bmp|webp|svg|ico)$/,
handler: "CacheFirst",
options: {
cacheName: "images",
expiration: {
maxEntries: 1000,
maxAgeSeconds: 60 * 60 * 24 * 365,
},
},
},
{
urlPattern: /\.(?:mp3|wav|m4a)$/,
handler: "CacheFirst",
options: {
cacheName: "audio",
expiration: {
maxEntries: 1000,
maxAgeSeconds: 60 * 60 * 24 * 365,
},
},
},
{
urlPattern: /\.(?:m4v|mpg|avi)$/,
handler: "CacheFirst",
options: {
cacheName: "videos",
expiration: {
maxEntries: 1000,
maxAgeSeconds: 60 * 60 * 24 * 365,
},
},
}
],
});
I tried InjectManifest and also tried it simplified via the recipes of the documentations of Workbox.

Related

ag-grid createRangeChart Line start from 0

I use createRangeChart to the line but i want that range wii start at 0 ,but i can't set lineDashOffset to anywhere
how can i use lineDashOffset in createRangeChart ?
enter image description here
I want 30.6 that one start to 0
onFirstDataRendered: function onFirstDataRendered(params) {
const colIds = params.columnApi
.getAllDisplayedColumns()
.map(col => col.getColId());
params.api.createRangeChart({
chartType: 'line',
// chartThemeOverrides: {
// common: {
// title: {
// enabled: true,
// text: message,
// },
// },
cellRange: {
columns: colIds,
},
suppressChartRanges: false,
chartContainer: document.querySelector("#datalistgridChart"+id),
});
i find min can set 0 in the
createRangeChart -> chartThemeOverrides -> cartesian -> axes -> number -> min:0
params.api.createRangeChart({
chartType: chartType,
chartThemeOverrides: {
cartesian: {
axes: {
number: {
label: {
formatter: (params) => {
if (chartType == 'line'){
return params.value+'%';
}
return params.value;
}
},
min:0,
},
},
},
common: {
legend: {
enabled: true,
position: 'bottom',
}
}
},
unlinkChart: true,
cellRange: {
columns: colIds,
},
seriesChartTypes:seriesChartTypes,
suppressChartRanges: false,
chartContainer: document.querySelector("#datalistgridChart"+id),
});

Invisible chart area during print and in exported PDF file in spreadsheet created by service account with Google Sheets API (Node.js) in shared folder

I'm creating a spreadsheet in Node.js environment in shared folder using service account with Google Sheets API v.4 in few steps:
Create spreadsheet itself in shared folder with "Can Edit" permission for service account.
Inserting some data and performing some text formats using spreadsheet ID received as callback from previous step.
Inserting chart using as income data the data inserted in prevoius step.
As the result I have a spreadsheet with expected result (text data and horizontal bar chart on same sheet). But when I'm trying to send it on printer, or download as PDF-file - chart area becomes completely invisible. I didn't find any option in official documentation about possible chart visibility during printing or something like this. And when I'm replacing this created chart with manually created one - everything is ok, I can print it and export to PDF.
So, what is the problem? Am I missing something? Or it's some bug?
index.js
const fs = require('fs');
const { google } = require('googleapis');
const express = require('express');
const bodyParser = require("body-parser");
const app = express();
const PORT = 3000;
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.listen(PORT, () => {
console.log(`Server started at http://localhost:${PORT}`)
})
const SCOPES = ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/spreadsheets'];
const FOLDER_ID = '1xG3xHhrucB4AGLmnd8T2TmCyqhmPux5Q';
var timeStamp = new Date().getTime();
console.log(`timeStamp at startup = ${timeStamp}`);
const auth = new google.auth.GoogleAuth({
keyFile: 'credentials.json',
scopes: SCOPES
});
process.env.HTTPS_PROXY = 'http://10.5.0.20:3128';
const sheets = google.sheets({
version: 'v4',
auth: auth,
proxy: 'http://10.5.0.20:3128'
});
const drive = google.drive({
version: 'v3',
auth: auth,
proxy: 'http://10.5.0.20:3128'
});
function createFileName() {
const now = new Date();
let date = String(now.toISOString().slice(0, 10));
let hours = String(now.getHours()).padStart(2, "0");
let minutes = String(now.getMinutes()).padStart(2, "0");
let seconds = String(now.getSeconds()).padStart(2, "0");
let humanDate = date.replaceAll('-', '.');
humanDate = `${humanDate}_${hours}-${minutes}-${seconds}`;
return humanDate;
}
async function saveFileLocally(filePath, data) {
fs.writeFile(filePath, JSON.stringify(data), error => {
if (error) {
console.error(error);
return;
}
});
return true;
}
app.get("/check", (req, res) => {
res.send('server is online...');
});
app.post("/motivation", async (req, res) => {
if(!req.body) return res.sendStatus(400);
try {
const prizv = req.body.prizv;
const name = req.body.name;
const father = req.body.father;
const sex = req.body.sex;
const age = req.body.age;
const factors = req.body.factors;
const testName = 'motivation';
const clientData = { prizv: prizv, name: name, father: father, sex: sex, age: age, factors: factors, testName: testName };
const fileName = `${createFileName()}_${prizv}`;
const filePath = `files/${testName}/${fileName}.txt`;
const isSaved = await saveFileLocally(filePath, clientData);
if (isSaved) {
console.log(`file is saved locally....`);
const sheetID = await createSheetToGoogleDIsk(clientData, fileName);
res.send(sheetID);
}
} catch (error) {
console.log(error)
}
});
async function createSheetToGoogleDIsk(clientData, fileName) {
const file = fileName;
var sheetsMetadata = {
name: file,
mimeType: 'application/vnd.google-apps.spreadsheet',
parents: [FOLDER_ID]
};
const res2 = drive.files.create({
resource: sheetsMetadata,
fields: 'id'
}, function (err, file) {
if (err) {
console.error(err);
} else {
console.log('SheetID: ', file.data.id);
const gSheetID = file.data.id;
pasteDataToGoogleSheet(clientData, gSheetID);
insertChartToGoogleSheet(gSheetID);
return file.data.id;
}
});
}
async function insertChartToGoogleSheet(spreadsheetId) {
spreadsheetId = spreadsheetId;
let requests = [];
// set font size for whole Sheet as 14
requests.push({
"repeatCell": {
"range": {
"sheetId": 0,
"startRowIndex": 0,
"endRowIndex": 100,
},
"cell": {
"userEnteredFormat": {
"textFormat": {
"fontSize": 13,
},
},
},
"fields": "userEnteredFormat.textFormat.fontSize"
},
});
// set header text format as Bold and 18 pt
requests.push({
"repeatCell": {
"range": {
"sheetId": 0,
"startRowIndex": 0,
"endRowIndex": 2,
},
"cell": {
"userEnteredFormat": {
"textFormat": {
"fontSize": 18,
"bold": true
},
},
},
"fields": "userEnteredFormat(textFormat)"
},
});
// set subheader text format as Bold and 15 pt
requests.push({
"repeatCell": {
"range": {
"sheetId": 0,
"startRowIndex": 3,
"endRowIndex": 4,
},
"cell": {
"userEnteredFormat": {
"textFormat": {
"fontSize": 15,
"bold": true
},
},
},
"fields": "userEnteredFormat(textFormat)"
},
});
// set client data as Bold
requests.push({
"repeatCell": {
"range": {
"sheetId": 0,
"startRowIndex": 5,
"endRowIndex": 10,
"startColumnIndex": 3,
"endColumnIndex": 5
},
"cell": {
"userEnteredFormat": {
"textFormat": {
"fontSize": 13,
"bold": true
},
},
},
"fields": "userEnteredFormat(textFormat)"
},
});
// set 1st column width as 20px
requests.push({
"updateDimensionProperties": {
"range": {
"sheetId": 0,
"dimension": "COLUMNS",
"startIndex": 0,
"endIndex": 1
},
"properties": {
"pixelSize": 20
},
"fields": "pixelSize"
}
});
// set 4st column width as 150px
requests.push({
"updateDimensionProperties": {
"range": {
"sheetId": 0,
"dimension": "COLUMNS",
"startIndex": 3,
"endIndex": 4
},
"properties": {
"pixelSize": 150
},
"fields": "pixelSize"
}
});
// set bold factors Values
requests.push({
"repeatCell": {
"range": {
"sheetId": 0,
"startRowIndex": 33,
"endRowIndex": 45,
"startColumnIndex": 4,
"endColumnIndex": 5
},
"cell": {
"userEnteredFormat": {
"textFormat": {
"fontSize": 13,
"bold": true
},
},
},
"fields": "userEnteredFormat(textFormat)"
},
});
requests.push({
"addChart": {
"chart": {
"chartId": 1,
"spec": {
"titleTextFormat": {
},
"basicChart": {
"chartType": "BAR",
"axis": [
{
"position": "BOTTOM_AXIS",
},
{
"position": "LEFT_AXIS",
}
],
"domains": [
{
"domain": {
"sourceRange": {
"sources": [
{
"sheetId": 0,
"startRowIndex": 33,
"endRowIndex": 45,
"startColumnIndex": 1,
"endColumnIndex": 2
}
]
},
},
}
],
"series": [
{
"series": {
"sourceRange": {
"sources": [
{
"sheetId": 0,
"startRowIndex": 33,
"endRowIndex": 45,
"startColumnIndex": 4,
"endColumnIndex": 5
}
]
}
},
"targetAxis": "BOTTOM_AXIS"
}
],
},
},
"position": {
"overlayPosition": {
"anchorCell": {
"sheetId": 0,
"rowIndex": 11,
"columnIndex": 1
},
"offsetXPixels": 0,
"offsetYPixels": -7,
"widthPixels": 800,
"heightPixels": 450
},
},
"border": {
"color": {
"red": 1,
"green": 1,
"blue": 1,
"alpha": 0
},
}
}
}
});
const batchUpdateRequest = { requests };
sheets.spreadsheets.batchUpdate({
spreadsheetId,
resource: batchUpdateRequest,
}, (err, result) => {
if (err) {
// Handle error
console.log(err);
return false
} else {
console.log(`${result.updatedCells} chart inserted`);
return spreadsheetId
}
});
}
async function pasteDataToGoogleSheet(clientData, sheetId) {
ecxelID = sheetId;
let data1 = [
["Тест «Мотиваційний особистісний профіль»"], [""], ["Результати тестування"], [""], ["Прізвище"], ["Імя"], ["По-батькові"], ["Вік"], ["Стать"]
];
let data2 = [
[clientData.prizv], [clientData.name], [clientData.father], [clientData.age], [clientData.sex]
];
let factorsLabels1_6 = [
["1. Матеріальна винагорода:"], ["2. Комфортні умови:"], ["3. Структурованість роботи:"], ["4. Соціальні контакти:"], ["5. Довірливі стосунки:"], ["6. Визнання:"]
];
let factors1_6 = [
[clientData.factors.factor1], [clientData.factors.factor2], [clientData.factors.factor3], [clientData.factors.factor4], [clientData.factors.factor5], [clientData.factors.factor6]
];
let factorsLabels7_12 = [
["7. Досягнення мети:"], ["8. Влада і вплив:"], ["9. Відсутність рутини:"], ["10. Креативність:"], ["11. Самовдосконалення і розвиток:"], ["12. Цікава і корисна діяльність:"]
];
let factors7_12 = [
[clientData.factors.factor7], [clientData.factors.factor8], [clientData.factors.factor9], [clientData.factors.factor10], [clientData.factors.factor11], [clientData.factors.factor12]
];
const data = [{
range: "B2:B10",
values: data1,
},
{
range: "D6:D10",
values: data2,
},
{
range: "B34:B39",
values: factorsLabels1_6,
},
{
range: "E34:E39",
values: factors1_6,
},
{
range: "B40:B45",
values: factorsLabels7_12,
},
{
range: "E40:E45",
values: factors7_12,
}
];
const resource = {
data,
valueInputOption: 'RAW',
};
sheets.spreadsheets.values.batchUpdate({
spreadsheetId: ecxelID,
resource: resource,
}, (err, result) => {
if (err) {
// Handle error
console.log(err);
return false
} else {
console.log(`${result.updatedCells} cells data inserted`);
return spreadsheetId;
}
});
}
I could confirm your situation. In this case, how about the following modification?
From:
"offsetXPixels": 0,
"offsetYPixels": -7,
"widthPixels": 800,
"heightPixels": 450
To:
"offsetXPixels": 0,
"offsetYPixels": 0, // Modified
"widthPixels": 800,
"heightPixels": 450
or
"widthPixels": 800,
"heightPixels": 450
When the values of offsetXPixels and offsetYPixels are the negative values, it was found that such an issue occurs.
When the values of offsetXPixels and offsetYPixels are 0, these values are not required to be included because of the default value.
Note:
When I tested the above modification, I could confirm that your issue could be removed.
Reference:
EmbeddedObjectPosition

AG Grid - Add rows of data to Master Detail without using JSON file

I wanted to know how to add rows of data to the master detail table but not using an external json file and just write the row records inline via the JS
Anyone have any idea on how to go about this
https://www.ag-grid.com/documentation/javascript/master-detail/
var gridOptions = {
columnDefs: [
// group cell renderer needed for expand / collapse icons
{ field: 'name', cellRenderer: 'agGroupCellRenderer' },
{ field: 'account' },
{ field: 'calls' },
{ field: 'minutes', valueFormatter: "x.toLocaleString() + 'm'" },
],
defaultColDef: {
flex: 1,
},
masterDetail: true,
detailCellRendererParams: {
detailGridOptions: {
columnDefs: [
{ field: 'callId' },
{ field: 'direction', minWidth: 150 },
{ field: 'number' },
{ field: 'duration', valueFormatter: "x.toLocaleString() + 's'" },
{ field: 'switchCode', minWidth: 150 },
],
defaultColDef: {
flex: 1,
},
},
getDetailRowData: function (params) {
// simulate delayed supply of data to the detail pane
setTimeout(function () {
params.successCallback(params.data.callRecords);
}, 1000);
},
},
};
// setup the grid after the page has finished loading
document.addEventListener('DOMContentLoaded', function () {
var gridDiv = document.querySelector('#myGrid');
new agGrid.Grid(gridDiv, gridOptions);
// I dont want to use the external json file
agGrid
.simpleHttpRequest({
url: 'https://www.ag-grid.com/example-assets/master-detail-data.json',
})
.then(function (data) {
gridOptions.api.setRowData(data);
});
});
Set the rowData field on the gridOptions like so:
var gridOptions = {
columnDefs: [
// group cell renderer needed for expand / collapse icons
{ field: 'name', cellRenderer: 'agGroupCellRenderer' },
{ field: 'account' },
{ field: 'calls' },
{ field: 'minutes', valueFormatter: "x.toLocaleString() + 'm'" },
],
defaultColDef: {
flex: 1,
},
masterDetail: true,
detailCellRendererParams: {
detailGridOptions: {
columnDefs: [
{ field: 'callId' },
{ field: 'direction' },
{ field: 'number', minWidth: 150 },
{ field: 'duration', valueFormatter: "x.toLocaleString() + 's'" },
{ field: 'switchCode', minWidth: 150 },
],
defaultColDef: {
flex: 1,
},
},
getDetailRowData: function (params) {
params.successCallback(params.data.callRecords);
},
},
onFirstDataRendered: onFirstDataRendered,
rowData: myData
};
In this instance, myData would look like this:
var myData = [
{
name: 'Nora Thomas',
account: 177000,
calls: 24,
minutes: 25.65,
callRecords: [
{
name: 'susan',
callId: 555,
duration: 72,
switchCode: 'SW3',
direction: 'Out',
number: '(00) 88542069',
},
],
},
];
Demo.

Why are the panels in my Grafana dashboard overlapping?

I am attempting to create a Grafana Scripted Dashboard consisting of panels that indicate whether a server is UP or not. The reason for this is that we constantly have a list of agents being added and making a new panel every time is becoming repetitive.
Currently, I have this as my script (Note my JavaScript/JQuery skills are abysmal..):
'use strict';
// accessible variables in this scope
var window, document, ARGS, $, jQuery, moment, kbn;
return function(callback) {
// Setup some variables
var dashboard;
// Initialize a skeleton with nothing but a rows array and service object
dashboard = {
annotations: {
list: [
{
builtIn: 1,
datasource: "-- Grafana --",
enable: true,
hide: false,
iconColor: "rgba(0, 211, 255, 1)",
name: "Annotations & Alerts",
type: "dashboard"
}
]
},
editable: true,
gnetId: null,
graphTooltip: 0,
id: 15,
links: [],
panels : [],
refresh: false,
schemaVersion: 16,
style: "dark",
tags: [],
templating: {
list: []
},
time: {
from: "now-5m",
to: "now"
},
timepicker: {
refresh_intervals: [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
time_options: [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
timezone: "",
title: "My Agents",
//uid: "000000019",
version: 2
};
// Set a title
dashboard.title = 'Scripted dash';
// Set default time
// time can be overridden in the url using from/to parameters, but this is
// handled automatically in grafana core during dashboard initialization
dashboard.time = {
from: "now-6h",
to: "now"
};
var rows = 1;
var seriesName = 'argName';
if(!_.isUndefined(ARGS.rows)) {
rows = parseInt(ARGS.rows, 10);
}
if(!_.isUndefined(ARGS.name)) {
seriesName = ARGS.name;
}
$.ajax({
method: 'GET',
url: 'http://my_server/api/my_rest_call_to_get_agents'
})
.done(function(result) {
//console.log(JSON.stringify(result));
var x = 0;
var y = 0;
var id = 0;
$.each(result.data.result, function(index, value) {
//console.log(value.metric.instance);
var panel = {
clusterName: "My Agent",
colorMode: "Panel",
colors: {
crit: "rgba(245, 54, 54, 0.9)",
disable: "rgba(128, 128, 128, 0.9)",
ok: "rgba(50, 128, 45, 0.9)",
warn: "rgba(237, 129, 40, 0.9)"
},
cornerRadius: 0,
//datasource: "Prometheus",
displayName: "Agent_Name",
flipCard: true,
flipTime: "2",
fontFormat: "Bold",
gridPos: {
h: 3,
w: 2,
x: x,
y: y
},
id: id,
isGrayOnNoData: true,
isHideAlertsOnDisable: false,
isIgnoreOKColors: false,
links: [],
targets: [
{
aggregation: "Last",
alias: value.metric.instance,
crit: 0,
decimals: 2,
displayAliasType: "Warning / Critical",
displayType: "Regular",
displayValueWithAlias: "Never",
expr: 'expression_to_query_service',
format: "time_series",
instant: true,
intervalFactor: 1,
legendFormat: value.metric.instance,
refId: "A",
units: "short",
valueHandler: "Number Threshold"
}
],
title: value.metric.instance,
transparent: true,
type: "vonage-status-panel"
}
dashboard.panels.push(panel);
x += 2;
});
callback(dashboard);
});
}
The strangest part about all of the panels being overlapping is that every panel has different coordinates if I look at each panel's JSON.
Any help with this would be appreciated!

Column chart has repeating x-axis label

I am at a loss on why Google column chart is repeating the x-axis label.
Please find the CodePen URL: https://codepen.io/anon/pen/MPOJQG?editors=0010
You may notice that I have tried both the approaches:
arrayToDataTable (line #4 in code pen)
conventional datatable structure (line #5 in code pen)
Following is the code from CodePen link:
//console.log("Loading current Google charts");
google.charts.load("current");
google.charts.setOnLoadCallback(function() {
//let dataTable = new google.visualization.arrayToDataTable(GetJSONArray()); //This also has the same issue
let dataTable = new google.visualization.DataTable(GetJSONData());
RenderChart(dataTable, "chart");
});
function RenderChart(dataTable, elementId) {
try {
const dateFormat = "MMM dd";
//debugger;
let numberOfRows = dataTable.getNumberOfRows();
let options = {
tooltip: { isHtml: true /*, trigger: 'selection'*/ },
height: 240,
legend: { position: "bottom" },
colors: ["#4CAF50"],
chartArea: { left: 80, top: 20, width: "90%" },
//isStacked: 'true',
hAxis: {
format: dateFormat
//gridlines: { count: numberOfRows }
},
vAxis: {
//format: '%',
title: "Percentage",
viewWindow: {
max: 100,
min: 0
}
}
};
if (numberOfRows === 1) {
//If there is only one date then Google chart messes up the chart, in that case it is must to set viewWindow
let hAxis = {
hAxis: {
viewWindow: {
min: dataTable.getValue(0, 0),
max: dataTable.getValue(numberOfRows - 1, 0)
}
}
};
options = $.extend(true, options, hAxis);
}
let wrapper = new google.visualization.ChartWrapper({
chartType: "ColumnChart",
dataTable: dataTable,
options: options,
containerId: elementId
});
wrapper.draw();
} catch (e) {
console.log(e.toString());
}
}
function GetJSONArray(){
let data = [
['Date', 'Pass', { role: 'annotation' } , {'type': 'string', 'role': 'tooltip', 'p': {'html': true}} ],
[new Date(2018, 9, 6),96, "48 (96.00%)", "<div>2018-10-06 (Sat)</div><div> - Pass: 48 (96.00%)</div><div> - Fail: 2 (4.00%)</div>"],
[new Date(2018, 9, 8),96.55172413793103448275862069,"168 (96.55%)","<div>2018-10-08 (Mon)</div><div> - Pass: 168 (96.55%)</div><div> - Fail: 6 (3.45%)</div>"],
[new Date(2018, 9, 9),95.82409460458240946045824095,"2,593 (95.82%)","<div>2018-10-09 (Tue)</div><div> - Pass: 2,593 (95.82%)</div><div> - Fail: 113 (4.18%)</div>"],
[new Date(2018, 9, 10),96.81303116147308781869688385,"2,734 (96.81%)","<div>2018-10-10 (Wed)</div><div> - Pass: 2,734 (96.81%)</div><div> - Fail: 90 (3.19%)</div>"],
[new Date(2018, 9, 11),96.80555555555555555555555556,"2,788 (96.81%)","<div>2018-10-11 (Thu)</div><div> - Pass: 2,788 (96.81%)</div><div> - Fail: 92 (3.19%)</div>"],
[new Date(2018, 9, 12),96.863295880149812734082397,"2,069 (96.86%)","<div>2018-10-12 (Fri)</div><div> - Pass: 2,069 (96.86%)</div><div> - Fail: 67 (3.14%)</div>"]
]
return data;
}
function GetJSONData() {
return {
cols: [
{ type: "date", id: "Date", label: "Date" },
{ type: "number", id: "Pass", label: "Pass %" },
{
type: "string",
id: "Annotation",
label: "Annotation",
p: { role: "annotation" }
},
{
type: "string",
id: "ToolTip",
label: "ToolTip",
p: { html: "true", role: "tooltip" }
}
],
rows: [
{
c: [
{ v: "Date(2018, 9, 6)" },
{ v: 96 },
{ v: "48 (96.00%)" },
{
v:
"<div>2018-10-06 (Sat)</div><div> - Pass: 48 (96.00%)</div><div> - Fail: 2 (4.00%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 8)" },
{ v: 96.55172413793103448275862069 },
{ v: "168 (96.55%)" },
{
v:
"<div>2018-10-08 (Mon)</div><div> - Pass: 168 (96.55%)</div><div> - Fail: 6 (3.45%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 9)" },
{ v: 95.82409460458240946045824095 },
{ v: "2,593 (95.82%)" },
{
v:
"<div>2018-10-09 (Tue)</div><div> - Pass: 2,593 (95.82%)</div><div> - Fail: 113 (4.18%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 10)" },
{ v: 96.81303116147308781869688385 },
{ v: "2,734 (96.81%)" },
{
v:
"<div>2018-10-10 (Wed)</div><div> - Pass: 2,734 (96.81%)</div><div> - Fail: 90 (3.19%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 11)" },
{ v: 96.80555555555555555555555556 },
{ v: "2,788 (96.81%)" },
{
v:
"<div>2018-10-11 (Thu)</div><div> - Pass: 2,788 (96.81%)</div><div> - Fail: 92 (3.19%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 12)" },
{ v: 96.863295880149812734082397 },
{ v: "2,069 (96.86%)" },
{
v:
"<div>2018-10-12 (Fri)</div><div> - Pass: 2,069 (96.86%)</div><div> - Fail: 67 (3.14%)</div>"
}
]
}
]
};
}
I have also referred following URLs:
Duplicate label on x-axis, stacking bar chart (google charts)
since you're using datetime for x-axis,
the chart doesn't know it should only show one label for each day.
instead, it adds dates to fill the range of the x-axis.
and since the format does not include time,
labels are repeated.
to correct, use option hAxis.ticks to provide your own labels.
to build dynamically, use data table method --> getColumnRange
this will return the min and max dates in the table.
then build an array of dates for each day.
let dateRange = dataTable.getColumnRange(0);
for (var i = dateRange.min.getTime(); i <= dateRange.max.getTime(); i = i + oneDay) {
hAxisTicks.push(new Date(i));
}
see following working snippet...
google.charts.load("current");
google.charts.setOnLoadCallback(function() {
//let dataTable = new google.visualization.arrayToDataTable(GetJSONArray()); //This also has the same issue
let dataTable = new google.visualization.DataTable(GetJSONData());
RenderChart(dataTable, "chart");
});
function RenderChart(dataTable, elementId) {
try {
const dateFormat = "MMM dd";
const oneDay = (1000 * 60 * 60 * 24);
//debugger;
let hAxisTicks = [];
let dateRange = dataTable.getColumnRange(0);
for (var i = dateRange.min.getTime(); i <= dateRange.max.getTime(); i = i + oneDay) {
hAxisTicks.push(new Date(i));
}
let numberOfRows = dataTable.getNumberOfRows();
let options = {
tooltip: { isHtml: true /*, trigger: 'selection'*/ },
height: 240,
legend: { position: "bottom" },
colors: ["#4CAF50"],
chartArea: { left: 80, top: 20, width: "90%" },
//isStacked: 'true',
hAxis: {
format: dateFormat,
ticks: hAxisTicks
//gridlines: { count: numberOfRows }
},
vAxis: {
//format: '%',
title: "Percentage",
viewWindow: {
max: 100,
min: 0
}
}
};
if (numberOfRows === 1) {
//If there is only one date then Google chart messes up the chart, in that case it is must to set viewWindow
let hAxis = {
hAxis: {
viewWindow: {
min: dataTable.getValue(0, 0),
max: dataTable.getValue(numberOfRows - 1, 0)
}
}
};
options = $.extend(true, options, hAxis);
}
let wrapper = new google.visualization.ChartWrapper({
chartType: "ColumnChart",
dataTable: dataTable,
options: options,
containerId: elementId
});
wrapper.draw();
} catch (e) {
console.log(e.toString());
}
}
function GetJSONArray(){
let data = [
['Date', 'Pass', { role: 'annotation' } , {'type': 'string', 'role': 'tooltip', 'p': {'html': true}} ],
[new Date(2018, 9, 6),96, "48 (96.00%)", "<div>2018-10-06 (Sat)</div><div> - Pass: 48 (96.00%)</div><div> - Fail: 2 (4.00%)</div>"],
[new Date(2018, 9, 8),96.55172413793103448275862069,"168 (96.55%)","<div>2018-10-08 (Mon)</div><div> - Pass: 168 (96.55%)</div><div> - Fail: 6 (3.45%)</div>"],
[new Date(2018, 9, 9),95.82409460458240946045824095,"2,593 (95.82%)","<div>2018-10-09 (Tue)</div><div> - Pass: 2,593 (95.82%)</div><div> - Fail: 113 (4.18%)</div>"],
[new Date(2018, 9, 10),96.81303116147308781869688385,"2,734 (96.81%)","<div>2018-10-10 (Wed)</div><div> - Pass: 2,734 (96.81%)</div><div> - Fail: 90 (3.19%)</div>"],
[new Date(2018, 9, 11),96.80555555555555555555555556,"2,788 (96.81%)","<div>2018-10-11 (Thu)</div><div> - Pass: 2,788 (96.81%)</div><div> - Fail: 92 (3.19%)</div>"],
[new Date(2018, 9, 12),96.863295880149812734082397,"2,069 (96.86%)","<div>2018-10-12 (Fri)</div><div> - Pass: 2,069 (96.86%)</div><div> - Fail: 67 (3.14%)</div>"]
]
return data;
}
function GetJSONData() {
return {
cols: [
{ type: "date", id: "Date", label: "Date" },
{ type: "number", id: "Pass", label: "Pass %" },
{
type: "string",
id: "Annotation",
label: "Annotation",
p: { role: "annotation" }
},
{
type: "string",
id: "ToolTip",
label: "ToolTip",
p: { html: "true", role: "tooltip" }
}
],
rows: [
{
c: [
{ v: "Date(2018, 9, 6)" },
{ v: 96 },
{ v: "48 (96.00%)" },
{
v:
"<div>2018-10-06 (Sat)</div><div> - Pass: 48 (96.00%)</div><div> - Fail: 2 (4.00%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 8)" },
{ v: 96.55172413793103448275862069 },
{ v: "168 (96.55%)" },
{
v:
"<div>2018-10-08 (Mon)</div><div> - Pass: 168 (96.55%)</div><div> - Fail: 6 (3.45%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 9)" },
{ v: 95.82409460458240946045824095 },
{ v: "2,593 (95.82%)" },
{
v:
"<div>2018-10-09 (Tue)</div><div> - Pass: 2,593 (95.82%)</div><div> - Fail: 113 (4.18%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 10)" },
{ v: 96.81303116147308781869688385 },
{ v: "2,734 (96.81%)" },
{
v:
"<div>2018-10-10 (Wed)</div><div> - Pass: 2,734 (96.81%)</div><div> - Fail: 90 (3.19%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 11)" },
{ v: 96.80555555555555555555555556 },
{ v: "2,788 (96.81%)" },
{
v:
"<div>2018-10-11 (Thu)</div><div> - Pass: 2,788 (96.81%)</div><div> - Fail: 92 (3.19%)</div>"
}
]
},
{
c: [
{ v: "Date(2018, 9, 12)" },
{ v: 96.863295880149812734082397 },
{ v: "2,069 (96.86%)" },
{
v:
"<div>2018-10-12 (Fri)</div><div> - Pass: 2,069 (96.86%)</div><div> - Fail: 67 (3.14%)</div>"
}
]
}
]
};
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart"></div>