├── .gitignore
├── README.md
├── now.json
├── package-lock.json
├── package.json
├── public
├── favicon.ico
└── index.html
└── src
├── App.css
├── App.js
├── App.test.js
├── components
├── Animation.js
├── BasicEmbed.js
├── DynamicLoad.js
├── Events.js
├── ExportPDF.js
├── Filter.js
├── GetData.js
├── Home.js
├── Resize.js
└── VizList.js
├── images
├── github.svg
└── tableau.png
├── index.css
├── index.js
├── navigation
├── Content.js
└── Main.js
└── serviceWorker.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
25 | .now
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tableau JS API React Playground
2 |
3 | ## Available Scripts
4 |
5 | ### `npm start`
6 |
7 | ### `npm run build`
8 |
9 | ## To-do
10 |
11 | - [x] Initialise Tableau --> destructure from the window
12 | - [x] Basic Embed
13 | - [x] Create component --> passing in props for title
14 | - [x] Install React Router
15 | - [x] Set up Route for Basic Embed and Dynamic Load
16 | - [x] Dynamic Load
17 | - [x] Export to PDF
18 | - [x] Filter
19 | - [x] Get Data
20 | - [x] Resize
21 | - [x] Respond to events
22 | - [x] Select Marks
23 | - [ ] Add link to Git repo
24 |
25 | - [ ] Create Tableau Embed Component from Hook
26 | - [x] Refactor the NavBar to a Component
27 |
--------------------------------------------------------------------------------
/now.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "name": "tableau-react-api",
4 | "alias": ["react-tableau-jsapi"],
5 | "builds": [
6 | {
7 | "src": "package.json",
8 | "use": "@now/static-build",
9 | "config": {
10 | "distDir": "build"
11 | }
12 | }
13 | ],
14 | "routes": [
15 | {
16 | "src": "^/static/(.*)",
17 | "headers": {
18 | "cache-control": "s-maxage=31536000,immutable"
19 | },
20 | "dest": "/static/$1"
21 | },
22 | {
23 | "src": "^/favicon.ico",
24 | "dest": "/favicon.ico"
25 | },
26 | {
27 | "src": "^/asset-manifest.json",
28 | "dest": "/asset-manifest.json"
29 | },
30 | {
31 | "src": "^/(.*)",
32 | "headers": {
33 | "cache-control": "s-maxage=0"
34 | },
35 | "dest": "/index.html"
36 | }
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-tableau-api-playground",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.13.1",
7 | "react-dom": "^16.13.1",
8 | "react-router-dom": "^4.3.1",
9 | "react-scripts": "2.1.5"
10 | },
11 | "scripts": {
12 | "start": "react-scripts start",
13 | "build": "react-scripts build",
14 | "now-build": "react-scripts build",
15 | "test": "react-scripts test",
16 | "eject": "react-scripts eject"
17 | },
18 | "eslintConfig": {
19 | "extends": "react-app"
20 | },
21 | "browserslist": [
22 | ">0.2%",
23 | "not dead",
24 | "not ie <= 11",
25 | "not op_mini all"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andre347/tableau-react-jsapi/8572f3ddf69394d038fba11488104a02b62bc600/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | React - Tableau JS API
11 |
12 |
13 | You need to enable JavaScript to run this app.
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
6 | body {
7 | font-family: Arial, Helvetica, sans-serif;
8 | background: #f8fafc;
9 | height: 100vh;
10 | line-height: 1.6;
11 | font-weight: 300;
12 | }
13 |
14 | .navBar {
15 | position: fixed;
16 | top: 0;
17 | z-index: 1;
18 | display: flex;
19 | width: 100%;
20 | height: 60px;
21 | background: rgba(0, 0, 0, 0.9);
22 | }
23 |
24 | .navBar ul {
25 | display: flex;
26 | list-style: none;
27 | width: 100%;
28 | justify-content: center;
29 | }
30 |
31 | .navBar li {
32 | padding: 1rem;
33 | margin: 0 1px;
34 | /* background-color: #333; */
35 | }
36 |
37 | .navBar ul li a {
38 | text-decoration: none;
39 | padding-bottom: 20px;
40 | text-transform: uppercase;
41 | color: #f4f4f4;
42 | }
43 |
44 | .navBar ul li a:hover {
45 | color: #e69500;
46 | border-bottom: 3px solid orange;
47 | }
48 |
49 | .App {
50 | text-align: center;
51 | display: flex;
52 | flex-direction: column;
53 | justify-content: center;
54 | align-items: center;
55 | }
56 |
57 | section {
58 | display: flex;
59 | flex-direction: column;
60 | align-items: center;
61 | justify-content: center;
62 | text-align: center;
63 | width: 100%;
64 | height: 100vh;
65 | }
66 |
67 | button {
68 | margin: 8px;
69 | padding: 2px;
70 | }
71 |
72 | input {
73 | margin: 3px;
74 | }
75 |
76 | @media only screen and (max-width: 915px) {
77 | .navBar ul li a {
78 | font-size: 12px;
79 | }
80 | .navBar ul li a:hover {
81 | color: #e69500;
82 | border-bottom: none;
83 | }
84 | }
85 |
86 | #githubLink {
87 | transition: all 0.2s ease-in-out;
88 | }
89 | #githubLink:hover {
90 | transform: scale(1.1);
91 | }
92 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Content from "./navigation/Content";
3 | import "./App.css";
4 |
5 | function App() {
6 | return ;
7 | }
8 |
9 | export default App;
10 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render( , div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/src/components/Animation.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | const { tableau } = window;
3 |
4 | function Animation() {
5 | const [url] = useState("https://public.tableau.com/views/AnimationDashboard_15616567350970/Sheet1");
6 | const [viz, setViz] = useState(null);
7 | const [timer, setTimer] = useState(1000);
8 | const [yearFilter, setYearFilter] = useState();
9 |
10 | const initViz = () => {
11 | const yearArr = [...Array(2011 + 1).keys()].slice(1960);
12 | setYearFilter(yearArr);
13 | let containerDiv = document.getElementById("container");
14 | const options = {
15 | Year: yearArr[0]
16 | };
17 | setViz(new tableau.Viz(containerDiv, url, options));
18 | };
19 |
20 | useEffect(initViz, []);
21 |
22 | let filterInterval;
23 |
24 | const startAnimate = async () => {
25 | console.log("Starting the animation...");
26 | const sheet = await viz.getWorkbook().activateSheetAsync("Sheet 1");
27 | let yearInterval = yearFilter[0];
28 | filterInterval = setInterval(() => {
29 | sheet.applyFilterAsync("Year", yearInterval, tableau.FilterUpdateType.REPLACE);
30 | yearInterval++;
31 | if (yearInterval === yearFilter[yearFilter.length - 1]) {
32 | clearInterval(filterInterval);
33 | }
34 | }, timer);
35 | };
36 |
37 | const stopAnimate = () => {
38 | console.log("Stopping the animation...");
39 | clearInterval(filterInterval);
40 | };
41 |
42 | return (
43 |
44 |
Animation
45 |
Every {timer} miliseconds
46 |
47 | Note: 1 second (1000ms - default speed) is about the fastest animation speed
48 |
49 |
setTimer(e.target.value)} />
50 |
startAnimate()}>
51 | Start
52 |
53 |
stopAnimate()}>
54 | Stop
55 |
56 |
57 |
58 | );
59 | }
60 |
61 | const setVizStyle = {
62 | margin: "25px",
63 | width: "800px",
64 | height: "700px"
65 | };
66 |
67 | export default Animation;
68 |
--------------------------------------------------------------------------------
/src/components/BasicEmbed.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useRef } from "react";
2 | const { tableau } = window;
3 |
4 | function BasicEmbed(props) {
5 | const [url] = useState(
6 | "https://public.tableau.com/views/RegionalSampleWorkbook/Storms"
7 | );
8 | const ref = useRef(null);
9 |
10 | const initViz = () => {
11 | new tableau.Viz(ref.current, url);
12 | };
13 |
14 | useEffect(initViz, []);
15 |
16 | return (
17 |
18 |
{props.location.state.title}
19 |
20 |
21 | );
22 | }
23 |
24 | const setVizStyle = {
25 | width: "800px",
26 | height: "700px",
27 | };
28 |
29 | export default BasicEmbed;
30 |
--------------------------------------------------------------------------------
/src/components/DynamicLoad.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import dashboards from "./VizList";
3 | const { tableau } = window;
4 |
5 | function DynamicLoad() {
6 | const [vizList] = useState(dashboards);
7 | const [vizCount, setVizCount] = useState(0);
8 | const [viz, setViz] = useState(null);
9 |
10 | const initViz = () => {
11 | let vizDiv = document.getElementById("vizContainer");
12 | let vizURL = vizList[vizCount];
13 | const options = {
14 | hideTabs: true
15 | };
16 | if (viz) {
17 | viz.dispose();
18 | setViz(null);
19 | }
20 | setViz(new tableau.Viz(vizDiv, vizURL, options));
21 | };
22 |
23 | useEffect(initViz, [vizCount]);
24 |
25 | return (
26 |
27 |
Dynamic Load
28 |
setVizCount(checkminValue(vizCount))}>Previous
29 |
setVizCount(checkmaxValue(vizCount, vizList.length))}>Next
30 |
31 |
32 | );
33 | }
34 |
35 | const checkminValue = value => {
36 | return value > 1 ? value - 1 : 0;
37 | };
38 |
39 | const checkmaxValue = (value, max) => {
40 | return value < max - 1 ? value + 1 : max - 1;
41 | };
42 |
43 | const setVizStyle = {
44 | margin: "25px",
45 | width: "800px",
46 | height: "700px"
47 | };
48 |
49 | export default DynamicLoad;
50 |
--------------------------------------------------------------------------------
/src/components/Events.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | const { tableau } = window;
3 |
4 | function Events() {
5 | const [url] = useState("https://public.tableau.com/views/RegionalSampleWorkbook/College");
6 | const [newViz, setViz] = useState(null);
7 | const [data, setData] = useState(null);
8 |
9 | function initViz() {
10 | let containerDiv = document.getElementById("container");
11 | const options = {
12 | "Academic Year": "",
13 | hideTabs: true,
14 | onFirstInteractive: () => {
15 | console.log("Getting started..");
16 | listenToMarksSelection();
17 |
18 | // when viz gets interactive, set the document title to the workbook name
19 | // when component unmounts it's being reset
20 | const workbookName = viz.getWorkbook().getName();
21 | document.title = workbookName;
22 | }
23 | };
24 | //const viz = new tableau.Viz(containerDiv, url, options);
25 | const viz = new tableau.Viz(containerDiv, url, options);
26 | setViz(viz);
27 |
28 | const listenToMarksSelection = () => {
29 | viz.addEventListener(tableau.TableauEventName.MARKS_SELECTION, onMarksSelection);
30 | };
31 | }
32 |
33 | useEffect(() => {
34 | initViz();
35 | // when unmounting the component, reset the document title
36 | return () => {
37 | console.log("Unmounting...");
38 | document.title = "React - Tableau JS API";
39 | };
40 | }, []);
41 |
42 | function onMarksSelection(marksEvent) {
43 | setData(null);
44 | marksEvent.getMarksAsync().then(marks => {
45 | for (let markIndex = 0; markIndex < marks.length; markIndex++) {
46 | const pairs = marks[markIndex].getPairs();
47 | setData(pairs);
48 | }
49 | });
50 | }
51 |
52 | function BuildList() {
53 | const items = data.map((e, i) => (
54 |
55 | Field Name:
56 | {e.fieldName}
57 |
58 | Value:
59 | {e.formattedValue}
60 |
61 | ));
62 | return items;
63 | }
64 |
65 | return (
66 |
67 |
Respond to Events
68 |
69 |
{data ? : null}
70 |
71 | );
72 | }
73 |
74 | const setVizStyle = {
75 | margin: "25px",
76 | width: "800px",
77 | height: "700px"
78 | };
79 |
80 | export default Events;
81 |
--------------------------------------------------------------------------------
/src/components/ExportPDF.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | const { tableau } = window;
3 |
4 | function ExportPDF() {
5 | const [url] = useState("https://public.tableau.com/views/RegionalSampleWorkbook/Obesity");
6 | const [viz, setViz] = useState(null);
7 |
8 | const initViz = () => {
9 | let containerDiv = document.getElementById("container");
10 | setViz(new tableau.Viz(containerDiv, url));
11 | };
12 |
13 | useEffect(initViz, []);
14 |
15 | const exportToPDF = () => {
16 | viz.showExportPDFDialog();
17 | };
18 |
19 | return (
20 |
21 |
Export PDF
22 |
Export PDF
23 |
24 |
25 | );
26 | }
27 |
28 | const setVizStyle = {
29 | margin: "25px",
30 | width: "800px",
31 | height: "700px"
32 | };
33 |
34 | export default ExportPDF;
35 |
--------------------------------------------------------------------------------
/src/components/Filter.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | const { tableau } = window;
3 |
4 | function Filter() {
5 | const [url] = useState("https://public.tableau.com/views/RegionalSampleWorkbook/College");
6 | const [viz, setViz] = useState(null);
7 | const [option, setOption] = useState("");
8 | const [optionList] = useState([
9 | {
10 | value: "",
11 | display: "All"
12 | },
13 | {
14 | value: "2013",
15 | display: "2013"
16 | },
17 | {
18 | value: "2014",
19 | display: "2014"
20 | }
21 | ]);
22 |
23 | const initViz = () => {
24 | console.log(optionList);
25 | let containerDiv = document.getElementById("container");
26 | const options = {
27 | "Academic Year": option
28 | };
29 | setViz(new tableau.Viz(containerDiv, url, options));
30 | };
31 |
32 | useEffect(initViz, []);
33 |
34 | const filterYear = year => {
35 | const sheet = viz.getWorkbook().getActiveSheet();
36 | if (year === "") {
37 | sheet.clearFilterAsync("Academic Year");
38 | setOption("");
39 | } else {
40 | sheet.applyFilterAsync("Academic Year", year, tableau.FilterUpdateType.REPLACE);
41 | setOption(year);
42 | }
43 | };
44 |
45 | return (
46 |
47 |
Filter
48 |
Select Academic Year
49 |
{
54 | filterYear(e.target.value);
55 | }}
56 | >
57 | {optionList.map(value => {
58 | return (
59 |
60 | {value.display}
61 |
62 | );
63 | })}
64 |
65 |
66 |
67 | );
68 | }
69 |
70 | const setVizStyle = {
71 | margin: "25px",
72 | width: "800px",
73 | height: "700px"
74 | };
75 |
76 | const selectStyle = {
77 | marginLeft: "10px"
78 | };
79 |
80 | export default Filter;
81 |
--------------------------------------------------------------------------------
/src/components/GetData.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | const { tableau } = window;
3 |
4 | function GetData() {
5 | const [url] = useState("https://public.tableau.com/views/RegionalSampleWorkbook/Storms");
6 | const [viz, setViz] = useState(null);
7 | const [sheet, setSheet] = useState(null);
8 | const [data, setData] = useState(null);
9 | const [rows, setRows] = useState(10);
10 | const [disabledState, setDisabled] = useState("disabled");
11 |
12 | const initViz = () => {
13 | let options = {
14 | hideTabs: true,
15 | hideToolbar: true,
16 | onFirstInteractive: () => {
17 | console.log("Enabling button..");
18 | setDisabled("");
19 | }
20 | };
21 | let containerDiv = document.getElementById("container");
22 | setViz(new tableau.Viz(containerDiv, url, options));
23 | };
24 |
25 | function GetUnderlyingData() {
26 | let selectedSheet = viz
27 | .getWorkbook()
28 | .getActiveSheet()
29 | .getWorksheets()
30 | .get("Storm Map Sheet");
31 |
32 | setSheet(selectedSheet);
33 |
34 | let options = {
35 | maxRows: rows,
36 | ignoreAliases: false,
37 | ignoreSelection: true,
38 | includeAllColumns: false
39 | };
40 |
41 | selectedSheet.getUnderlyingDataAsync(options).then(t => {
42 | setData(t.getData());
43 | });
44 | }
45 |
46 | useEffect(initViz, []);
47 |
48 | function BuildTable() {
49 | return {JSON.stringify(data)}
;
50 | }
51 |
52 | return (
53 |
54 |
Get Data
55 |
GetUnderlyingData()}>
56 | Get Data
57 |
58 |
{
62 | setRows(e.target.value);
63 | }}
64 | />
65 |
66 |
{data ? : null}
67 |
68 | );
69 | }
70 |
71 | const setVizStyle = {
72 | margin: "25px",
73 | width: "600px",
74 | height: "650px"
75 | };
76 |
77 | export default GetData;
78 |
--------------------------------------------------------------------------------
/src/components/Home.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import logo from "../images/tableau.png";
3 | import gitHubLogo from "../images/github.svg";
4 | //public / static / images / tableau.png
5 |
6 | export default function Home() {
7 | return (
8 |
9 |
10 |
React Playground for Tableau JavaScript API
11 |
by Andre de Vries
12 |
👇
13 |
14 | {" "}
15 |
16 |
17 |
18 | );
19 | }
20 |
21 | const githubStyle = {
22 | width: "50px",
23 | height: "50px"
24 | };
25 |
26 | const indicatorStyle = {
27 | fontSize: "3rem",
28 | width: "100%",
29 | textAlign: "center"
30 | };
31 |
--------------------------------------------------------------------------------
/src/components/Resize.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | const { tableau } = window;
3 |
4 | function Resize() {
5 | const [url] = useState("https://public.tableau.com/views/RegionalSampleWorkbook/Stocks");
6 | const [viz, setViz] = useState(null);
7 | const [width, setWidth] = useState(600);
8 | const [height, setHeight] = useState(650);
9 |
10 | const initViz = () => {
11 | let options = {
12 | hideTabs: true,
13 | onFirstInteractive: () => {
14 | console.log("Loading..");
15 | }
16 | };
17 | let containerDiv = document.getElementById("container");
18 | setViz(new tableau.Viz(containerDiv, url, options));
19 | };
20 |
21 | const setVizStyle = {
22 | margin: "25px",
23 | width: width + 25,
24 | height: height + 25
25 | };
26 |
27 | function SetDimensions() {
28 | viz.setFrameSize(parseInt(width, 10), parseInt(height, 10));
29 | }
30 |
31 | useEffect(initViz, []);
32 |
33 | return (
34 |
35 |
Resize
36 |
{
40 | setWidth(e.target.value);
41 | }}
42 | />
43 |
{
47 | setHeight(e.target.value);
48 | }}
49 | />
50 |
SetDimensions()}>
51 | Resize
52 |
53 |
54 |
55 | );
56 | }
57 |
58 | export default Resize;
59 |
--------------------------------------------------------------------------------
/src/components/VizList.js:
--------------------------------------------------------------------------------
1 | const dashboards = [
2 | "https://public.tableau.com/views/RegionalSampleWorkbook/Flights",
3 | "https://public.tableau.com/views/RegionalSampleWorkbook/Obesity",
4 | "https://public.tableau.com/views/RegionalSampleWorkbook/College",
5 | "https://public.tableau.com/views/RegionalSampleWorkbook/Stocks",
6 | "https://public.tableau.com/views/RegionalSampleWorkbook/Storms"
7 | ];
8 |
9 | export default dashboards;
10 |
--------------------------------------------------------------------------------
/src/images/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/images/tableau.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andre347/tableau-react-jsapi/8572f3ddf69394d038fba11488104a02b62bc600/src/images/tableau.png
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./index.css";
4 | import App from "./App";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/src/navigation/Content.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { BrowserRouter as Router, Route, Link } from "react-router-dom";
3 |
4 | import Main from "./Main";
5 |
6 | function Header() {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 | Home
14 |
15 |
16 |
24 | Basic Embed
25 |
26 |
27 |
28 | Dynamic Load
29 |
30 |
31 | Export PDF
32 |
33 |
34 | Filter
35 |
36 |
37 | Get Data
38 |
39 |
40 | Resize
41 |
42 |
43 | Events
44 |
45 |
46 | Animation
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | );
55 | }
56 |
57 | export default Header;
58 |
--------------------------------------------------------------------------------
/src/navigation/Main.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { BrowserRouter as Router, Route } from "react-router-dom";
3 |
4 | import BasicEmbed from "../components/BasicEmbed";
5 | import DynamicLoad from "../components/DynamicLoad";
6 | import ExportPDF from "../components/ExportPDF";
7 | import Filter from "../components/Filter";
8 | import GetData from "../components/GetData";
9 | import Resize from "../components/Resize";
10 | import Events from "../components/Events";
11 | import Home from "../components/Home";
12 | import Animation from "../components/Animation";
13 |
14 | function Main() {
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | {/* testing sending string prop and destructure in component */}
27 |
28 | );
29 | }
30 |
31 | export default Main;
32 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read http://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit http://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See http://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------