├── .eslintignore ├── .eslintrc ├── .github └── workflows │ └── playwright.yml ├── .gitignore ├── .hygen.js ├── .hygen └── new │ └── component │ ├── component.tsx.ejs.t │ ├── index.js │ ├── index.tsx.ejs.t │ ├── styles.scss.ejs.t │ └── test.js.ejs.t ├── .nvmrc ├── .prettierignore ├── .prettierrc.json ├── .unimportedrc.json ├── LICENSE.md ├── README.md ├── _examples ├── gapminder_js │ ├── gapminder.json │ └── id └── gapminder_testing │ ├── config.jsonp │ ├── displays │ ├── Life_expectancy │ │ ├── displayInfo.jsonp │ │ ├── metaData.js │ │ └── panels │ │ │ ├── html_panel │ │ │ ├── Afghanistan.html │ │ │ ├── Albania.html │ │ │ ├── Algeria.html │ │ │ ├── Angola.html │ │ │ ├── Argentina.html │ │ │ ├── Australia.html │ │ │ ├── Austria.html │ │ │ ├── Bahrain.html │ │ │ ├── Bangladesh.html │ │ │ ├── Belgium.html │ │ │ ├── Benin.html │ │ │ ├── Bolivia.html │ │ │ ├── Bosnia_and_Herzegovina.html │ │ │ ├── Botswana.html │ │ │ ├── Brazil.html │ │ │ ├── Bulgaria.html │ │ │ ├── Burkina_Faso.html │ │ │ ├── Burundi.html │ │ │ ├── Cambodia.html │ │ │ ├── Cameroon.html │ │ │ ├── Canada.html │ │ │ ├── Central_African_Republic.html │ │ │ ├── Chad.html │ │ │ ├── Chile.html │ │ │ ├── China.html │ │ │ ├── Colombia.html │ │ │ ├── Comoros.html │ │ │ ├── Congo__Dem__Rep_.html │ │ │ ├── Congo__Rep_.html │ │ │ ├── Costa_Rica.html │ │ │ ├── Cote_d_Ivoire.html │ │ │ ├── Croatia.html │ │ │ ├── Cuba.html │ │ │ ├── Czech_Republic.html │ │ │ ├── Denmark.html │ │ │ ├── Djibouti.html │ │ │ ├── Dominican_Republic.html │ │ │ ├── Ecuador.html │ │ │ ├── Egypt.html │ │ │ ├── El_Salvador.html │ │ │ ├── Equatorial_Guinea.html │ │ │ ├── Eritrea.html │ │ │ ├── Ethiopia.html │ │ │ ├── Finland.html │ │ │ ├── France.html │ │ │ ├── Gabon.html │ │ │ ├── Gambia.html │ │ │ ├── Germany.html │ │ │ ├── Ghana.html │ │ │ ├── Greece.html │ │ │ ├── Guatemala.html │ │ │ ├── Guinea.html │ │ │ ├── Guinea_Bissau.html │ │ │ ├── Haiti.html │ │ │ ├── Honduras.html │ │ │ ├── Hong_Kong__China.html │ │ │ ├── Hungary.html │ │ │ ├── Iceland.html │ │ │ ├── India.html │ │ │ ├── Indonesia.html │ │ │ ├── Iran.html │ │ │ ├── Iraq.html │ │ │ ├── Ireland.html │ │ │ ├── Israel.html │ │ │ ├── Italy.html │ │ │ ├── Jamaica.html │ │ │ ├── Japan.html │ │ │ ├── Jordan.html │ │ │ ├── Kenya.html │ │ │ ├── Korea__Dem__Rep_.html │ │ │ ├── Korea__Rep_.html │ │ │ ├── Kuwait.html │ │ │ ├── Lebanon.html │ │ │ ├── Lesotho.html │ │ │ ├── Liberia.html │ │ │ ├── Libya.html │ │ │ ├── Madagascar.html │ │ │ ├── Malawi.html │ │ │ ├── Malaysia.html │ │ │ ├── Mali.html │ │ │ ├── Mauritania.html │ │ │ ├── Mauritius.html │ │ │ ├── Mexico.html │ │ │ ├── Mongolia.html │ │ │ ├── Montenegro.html │ │ │ ├── Morocco.html │ │ │ ├── Mozambique.html │ │ │ ├── Myanmar.html │ │ │ ├── Namibia.html │ │ │ ├── Nepal.html │ │ │ ├── Netherlands.html │ │ │ ├── New_Zealand.html │ │ │ ├── Nicaragua.html │ │ │ ├── Niger.html │ │ │ ├── Nigeria.html │ │ │ ├── Norway.html │ │ │ ├── Oman.html │ │ │ ├── Pakistan.html │ │ │ ├── Panama.html │ │ │ ├── Paraguay.html │ │ │ ├── Peru.html │ │ │ ├── Philippines.html │ │ │ ├── Poland.html │ │ │ ├── Portugal.html │ │ │ ├── Puerto_Rico.html │ │ │ ├── Reunion.html │ │ │ ├── Romania.html │ │ │ ├── Rwanda.html │ │ │ ├── Sao_Tome_and_Principe.html │ │ │ ├── Saudi_Arabia.html │ │ │ ├── Senegal.html │ │ │ ├── Serbia.html │ │ │ ├── Sierra_Leone.html │ │ │ ├── Singapore.html │ │ │ ├── Slovak_Republic.html │ │ │ ├── Slovenia.html │ │ │ ├── Somalia.html │ │ │ ├── South_Africa.html │ │ │ ├── Spain.html │ │ │ ├── Sri_Lanka.html │ │ │ ├── Sudan.html │ │ │ ├── Swaziland.html │ │ │ ├── Sweden.html │ │ │ ├── Switzerland.html │ │ │ ├── Syria.html │ │ │ ├── Taiwan.html │ │ │ ├── Tanzania.html │ │ │ ├── Thailand.html │ │ │ ├── Togo.html │ │ │ ├── Trinidad_and_Tobago.html │ │ │ ├── Tunisia.html │ │ │ ├── Turkey.html │ │ │ ├── Uganda.html │ │ │ ├── United_Kingdom.html │ │ │ ├── United_States.html │ │ │ ├── Uruguay.html │ │ │ ├── Venezuela.html │ │ │ ├── Vietnam.html │ │ │ ├── West_Bank_and_Gaza.html │ │ │ ├── Yemen__Rep_.html │ │ │ ├── Zambia.html │ │ │ └── Zimbabwe.html │ │ │ └── lexp_time │ │ │ ├── Afghanistan_Asia.svg │ │ │ ├── Albania_Europe.svg │ │ │ ├── Algeria_Africa.svg │ │ │ ├── Angola_Africa.svg │ │ │ ├── Argentina_Americas.svg │ │ │ ├── Australia_Oceania.svg │ │ │ ├── Austria_Europe.svg │ │ │ ├── Bahrain_Asia.svg │ │ │ ├── Bangladesh_Asia.svg │ │ │ ├── Belgium_Europe.svg │ │ │ ├── Benin_Africa.svg │ │ │ ├── Bolivia_Americas.svg │ │ │ ├── Bosnia_and_Herzegovina_Europe.svg │ │ │ ├── Botswana_Africa.svg │ │ │ ├── Brazil_Americas.svg │ │ │ ├── Bulgaria_Europe.svg │ │ │ ├── Burkina_Faso_Africa.svg │ │ │ ├── Burundi_Africa.svg │ │ │ ├── Cambodia_Asia.svg │ │ │ ├── Cameroon_Africa.svg │ │ │ ├── Canada_Americas.svg │ │ │ ├── Central_African_Republic_Africa.svg │ │ │ ├── Chad_Africa.svg │ │ │ ├── Chile_Americas.svg │ │ │ ├── China_Asia.svg │ │ │ ├── Colombia_Americas.svg │ │ │ ├── Comoros_Africa.svg │ │ │ ├── Congo__Dem__Rep__Africa.svg │ │ │ ├── Congo__Rep__Africa.svg │ │ │ ├── Costa_Rica_Americas.svg │ │ │ ├── Cote_d_Ivoire_Africa.svg │ │ │ ├── Croatia_Europe.svg │ │ │ ├── Cuba_Americas.svg │ │ │ ├── Czech_Republic_Europe.svg │ │ │ ├── Denmark_Europe.svg │ │ │ ├── Djibouti_Africa.svg │ │ │ ├── Dominican_Republic_Americas.svg │ │ │ ├── Ecuador_Americas.svg │ │ │ ├── Egypt_Africa.svg │ │ │ ├── El_Salvador_Americas.svg │ │ │ ├── Equatorial_Guinea_Africa.svg │ │ │ ├── Eritrea_Africa.svg │ │ │ ├── Ethiopia_Africa.svg │ │ │ ├── Finland_Europe.svg │ │ │ ├── France_Europe.svg │ │ │ ├── Gabon_Africa.svg │ │ │ ├── Gambia_Africa.svg │ │ │ ├── Germany_Europe.svg │ │ │ ├── Ghana_Africa.svg │ │ │ ├── Greece_Europe.svg │ │ │ ├── Guatemala_Americas.svg │ │ │ ├── Guinea_Africa.svg │ │ │ ├── Guinea_Bissau_Africa.svg │ │ │ ├── Haiti_Americas.svg │ │ │ ├── Honduras_Americas.svg │ │ │ ├── Hong_Kong__China_Asia.svg │ │ │ ├── Hungary_Europe.svg │ │ │ ├── Iceland_Europe.svg │ │ │ ├── India_Asia.svg │ │ │ ├── Indonesia_Asia.svg │ │ │ ├── Iran_Asia.svg │ │ │ ├── Iraq_Asia.svg │ │ │ ├── Ireland_Europe.svg │ │ │ ├── Israel_Asia.svg │ │ │ ├── Italy_Europe.svg │ │ │ ├── Jamaica_Americas.svg │ │ │ ├── Japan_Asia.svg │ │ │ ├── Jordan_Asia.svg │ │ │ ├── Kenya_Africa.svg │ │ │ ├── Korea__Dem__Rep__Asia.svg │ │ │ ├── Korea__Rep__Asia.svg │ │ │ ├── Kuwait_Asia.svg │ │ │ ├── Lebanon_Asia.svg │ │ │ ├── Lesotho_Africa.svg │ │ │ ├── Liberia_Africa.svg │ │ │ ├── Libya_Africa.svg │ │ │ ├── Madagascar_Africa.svg │ │ │ ├── Malawi_Africa.svg │ │ │ ├── Malaysia_Asia.svg │ │ │ ├── Mali_Africa.svg │ │ │ ├── Mauritania_Africa.svg │ │ │ ├── Mauritius_Africa.svg │ │ │ ├── Mexico_Americas.svg │ │ │ ├── Mongolia_Asia.svg │ │ │ ├── Montenegro_Europe.svg │ │ │ ├── Morocco_Africa.svg │ │ │ ├── Mozambique_Africa.svg │ │ │ ├── Myanmar_Asia.svg │ │ │ ├── Namibia_Africa.svg │ │ │ ├── Nepal_Asia.svg │ │ │ ├── Netherlands_Europe.svg │ │ │ ├── New_Zealand_Oceania.svg │ │ │ ├── Nicaragua_Americas.svg │ │ │ ├── Niger_Africa.svg │ │ │ ├── Nigeria_Africa.svg │ │ │ ├── Norway_Europe.svg │ │ │ ├── Oman_Asia.svg │ │ │ ├── Pakistan_Asia.svg │ │ │ ├── Panama_Americas.svg │ │ │ ├── Paraguay_Americas.svg │ │ │ ├── Peru_Americas.svg │ │ │ ├── Philippines_Asia.svg │ │ │ ├── Poland_Europe.svg │ │ │ ├── Portugal_Europe.svg │ │ │ ├── Puerto_Rico_Americas.svg │ │ │ ├── Reunion_Africa.svg │ │ │ ├── Romania_Europe.svg │ │ │ ├── Rwanda_Africa.svg │ │ │ ├── Sao_Tome_and_Principe_Africa.svg │ │ │ ├── Saudi_Arabia_Asia.svg │ │ │ ├── Senegal_Africa.svg │ │ │ ├── Serbia_Europe.svg │ │ │ ├── Sierra_Leone_Africa.svg │ │ │ ├── Singapore_Asia.svg │ │ │ ├── Slovak_Republic_Europe.svg │ │ │ ├── Slovenia_Europe.svg │ │ │ ├── Somalia_Africa.svg │ │ │ ├── South_Africa_Africa.svg │ │ │ ├── Spain_Europe.svg │ │ │ ├── Sri_Lanka_Asia.svg │ │ │ ├── Sudan_Africa.svg │ │ │ ├── Swaziland_Africa.svg │ │ │ ├── Sweden_Europe.svg │ │ │ ├── Switzerland_Europe.svg │ │ │ ├── Syria_Asia.svg │ │ │ ├── Taiwan_Asia.svg │ │ │ ├── Tanzania_Africa.svg │ │ │ ├── Thailand_Asia.svg │ │ │ ├── Togo_Africa.svg │ │ │ ├── Trinidad_and_Tobago_Americas.svg │ │ │ ├── Tunisia_Africa.svg │ │ │ ├── Turkey_Europe.svg │ │ │ ├── Uganda_Africa.svg │ │ │ ├── United_Kingdom_Europe.svg │ │ │ ├── United_States_Americas.svg │ │ │ ├── Uruguay_Americas.svg │ │ │ ├── Venezuela_Americas.svg │ │ │ ├── Vietnam_Asia.svg │ │ │ ├── West_Bank_and_Gaza_Asia.svg │ │ │ ├── Yemen__Rep__Asia.svg │ │ │ ├── Zambia_Africa.svg │ │ │ └── Zimbabwe_Africa.svg │ ├── Life_expectancy_by_continent │ │ ├── displayInfo.jsonp │ │ ├── metaData.js │ │ └── panels │ │ │ └── lexp_time │ │ │ ├── Africa.png │ │ │ ├── Africa.svg │ │ │ ├── Americas.png │ │ │ ├── Americas.svg │ │ │ ├── Asia.png │ │ │ ├── Asia.svg │ │ │ ├── Europe.png │ │ │ ├── Europe.svg │ │ │ ├── Oceania.png │ │ │ └── Oceania.svg │ ├── displayList.jsonp │ └── libs │ │ └── mustachewidget │ │ ├── htmlwidgets-1.6.4 │ │ └── htmlwidgets.js │ │ ├── mustache-4.2.0 │ │ └── mustache.min.js │ │ └── mustachewidget-binding-0.0.0.9000 │ │ └── mustachewidget.js │ ├── id │ └── index.html ├── cors.js ├── deploy.sh ├── index.html ├── package-lock.json ├── package.json ├── playwright.config.ts ├── public ├── favicon.ico ├── index.html ├── manifest.json └── mockServiceWorker.js ├── src ├── App.tsx ├── CrossfilterClient.ts ├── DataClient.ts ├── TrelliscopeApp.tsx ├── assets │ └── styles │ │ ├── main.css │ │ └── variables.scss ├── classManipulation.ts ├── components │ ├── AddViewModal │ │ ├── AddViewModal.module.scss │ │ ├── AddViewModal.tsx │ │ └── index.ts │ ├── CatHistogram │ │ ├── CatHistogram.module.scss │ │ ├── CatHistogram.tsx │ │ ├── CatHistogramBar.tsx │ │ └── index.ts │ ├── Chip │ │ ├── Chip.module.scss │ │ ├── Chip.tsx │ │ └── index.ts │ ├── ColumnSelector │ │ ├── ColumnSelector.module.scss │ │ ├── ColumnSelector.tsx │ │ └── index.ts │ ├── ComposeEmail │ │ ├── ComposeEmail.module.scss │ │ ├── ComposeEmail.tsx │ │ └── index.ts │ ├── ConfirmationModal │ │ ├── ConfirmationModal.module.scss │ │ ├── ConfirmationModal.tsx │ │ └── index.ts │ ├── Content │ │ ├── Content.module.scss │ │ ├── Content.tsx │ │ └── index.ts │ ├── ContentContainer │ │ ├── ContentContainer.module.scss │ │ ├── ContentContainer.tsx │ │ └── index.ts │ ├── ContentHeader │ │ ├── ContentHeader.module.scss │ │ ├── ContentHeader.tsx │ │ └── index.ts │ ├── Credits │ │ ├── Credits.module.scss │ │ ├── Credits.tsx │ │ └── index.ts │ ├── DataProvider │ │ └── index.tsx │ ├── DataTable │ │ ├── DataTable.module.scss │ │ ├── DataTable.tsx │ │ └── index.ts │ ├── DisplayInfo │ │ ├── DisplayInfo.module.scss │ │ ├── DisplayInfo.tsx │ │ └── index.ts │ ├── DisplaySelect │ │ ├── DisplaySelect.module.scss │ │ ├── DisplaySelect.tsx │ │ └── index.ts │ ├── DownloadCsv │ │ ├── DownloadCsv.module.scss │ │ ├── DownloadCsv.tsx │ │ └── index.ts │ ├── Ellipsis │ │ ├── Ellipsis.module.scss │ │ ├── Ellipsis.tsx │ │ └── index.ts │ ├── ErrorSnack │ │ ├── ErrorSnack.module.scss │ │ ├── ErrorSnack.tsx │ │ └── index.ts │ ├── ErrorWrapper │ │ ├── ErrorWrapper.module.scss │ │ ├── ErrorWrapper.tsx │ │ └── index.ts │ ├── ExportInputDialog │ │ ├── ExportInputDialog.module.scss │ │ ├── ExportInputDialog.tsx │ │ └── index.ts │ ├── ExportViewsModal │ │ ├── ExportViewsModal.module.scss │ │ ├── ExportViewsModal.tsx │ │ └── index.ts │ ├── FilterCat │ │ ├── FilterCat.module.scss │ │ ├── FilterCat.tsx │ │ └── index.ts │ ├── FilterDateRange │ │ ├── FilterDateRange.module.scss │ │ ├── FilterDateRange.tsx │ │ └── index.ts │ ├── FilterDateTimeRange │ │ ├── FilterDateTimeRange.module.scss │ │ ├── FilterDateTimeRange.tsx │ │ └── index.ts │ ├── FilterInput │ │ ├── FilterInput.module.scss │ │ ├── FilterInput.tsx │ │ └── index.ts │ ├── FilterNum │ │ ├── FilterNum.module.scss │ │ ├── FilterNum.tsx │ │ └── index.ts │ ├── Filters │ │ ├── Filters.module.scss │ │ ├── Filters.tsx │ │ └── index.ts │ ├── FormattedNumber │ │ ├── FormattedNumber.test.js │ │ ├── FormattedNumber.tsx │ │ └── index.ts │ ├── FullscreenButton │ │ ├── FullscreenButton.module.scss │ │ ├── FullscreenButton.tsx │ │ └── index.ts │ ├── Header │ │ ├── Header.module.scss │ │ ├── Header.tsx │ │ └── index.ts │ ├── HelpInfo │ │ ├── HelpInfo.module.scss │ │ ├── HelpInfo.test.js │ │ ├── HelpInfo.tsx │ │ └── index.ts │ ├── HowToUse │ │ ├── HowToUse.module.scss │ │ ├── HowToUse.tsx │ │ └── index.ts │ ├── ImportViewsModal │ │ ├── ImportViewsModal.module.scss │ │ ├── ImportViewsModal.tsx │ │ └── index.ts │ ├── Labels │ │ ├── Labels.module.scss │ │ ├── Labels.tsx │ │ └── index.ts │ ├── LayoutSelector │ │ ├── LayoutSelector.module.scss │ │ ├── LayoutSelector.tsx │ │ └── index.ts │ ├── NumHistogram │ │ ├── NumHistogram.module.scss │ │ ├── NumHistogram.tsx │ │ ├── NumHistogramAxis.tsx │ │ ├── NumHistogramBar.tsx │ │ ├── NumHistogramBrush.tsx │ │ └── index.ts │ ├── Pagination │ │ ├── Pagination.module.scss │ │ ├── Pagination.tsx │ │ └── index.ts │ ├── Panel │ │ ├── Panel.module.scss │ │ ├── Panel.test.js │ │ ├── Panel.tsx │ │ ├── PanelGraphic.tsx │ │ ├── PanelGraphicWrapper.tsx │ │ └── index.ts │ ├── PanelDialog │ │ ├── PanelDialog.module.scss │ │ ├── PanelDialog.tsx │ │ └── index.ts │ ├── PanelInputs │ │ ├── PanelInputCheckbox.tsx │ │ ├── PanelInputMultiSelect.tsx │ │ ├── PanelInputRadios.tsx │ │ ├── PanelInputSelect.tsx │ │ ├── PanelInputText.tsx │ │ ├── PanelInputs.module.scss │ │ └── index.ts │ ├── PanelLabels │ │ ├── PanelLabels.module.scss │ │ ├── PanelLabels.tsx │ │ ├── PanelLabelsCell.tsx │ │ └── index.ts │ ├── PanelPicker │ │ ├── PanelPicker.module.scss │ │ ├── PanelPicker.tsx │ │ └── index.ts │ ├── PanelZoomLabels │ │ ├── PanelZoomLabels.module.scss │ │ ├── PanelZoomLabels.tsx │ │ ├── PanelZoomLabelsCell.tsx │ │ ├── PanelZoomLabelsCellContent.tsx │ │ └── index.ts │ ├── Share │ │ ├── Share.module.scss │ │ ├── Share.tsx │ │ └── index.ts │ ├── Shortcuts │ │ ├── Shortcuts.module.scss │ │ ├── Shortcuts.tsx │ │ └── index.ts │ ├── Sidebar │ │ ├── Sidebar.module.scss │ │ ├── Sidebar.tsx │ │ └── index.ts │ ├── Sort │ │ ├── Sort.module.scss │ │ ├── Sort.tsx │ │ └── index.ts │ ├── Tour │ │ ├── Tour.module.scss │ │ ├── Tour.tsx │ │ └── index.ts │ ├── UserInfo │ │ ├── UserInfo.module.scss │ │ ├── UserInfo.tsx │ │ └── index.ts │ ├── VariableSelector │ │ ├── VariableSelector.module.scss │ │ ├── VariableSelector.tsx │ │ └── index.ts │ └── Views │ │ ├── Views.module.scss │ │ ├── Views.tsx │ │ └── index.ts ├── constants.ts ├── getCustomProperties.tsx ├── index.tsx ├── inputUtils.ts ├── jsApi.ts ├── middleware │ ├── callbackMiddleware.ts │ └── hash.ts ├── palette.ts ├── reducers │ └── index.ts ├── selectors │ ├── app.ts │ ├── hash.ts │ ├── index.ts │ ├── ui.ts │ └── useMetaInfo.ts ├── setupTests.js ├── slices │ ├── appSlice.ts │ ├── cogDataMutableSlice.ts │ ├── configAPI.ts │ ├── configSlice.ts │ ├── displayInfoAPI.ts │ ├── displayListAPI.ts │ ├── filterSlice.ts │ ├── htmlAPI.ts │ ├── labelsSlice.ts │ ├── layoutSlice.ts │ ├── metaDataAPI.ts │ ├── relDispPositionsSlice.ts │ ├── selectedDisplaySlice.ts │ ├── selectedRelDispsSlice.ts │ ├── sortSlice.ts │ └── uiSlice.ts ├── store.ts ├── test-utils.js ├── test │ ├── __mockData__ │ │ ├── mockFunctions.js │ │ ├── restHandlers.js │ │ ├── server.js │ │ └── worker.js │ ├── appError.spec.js │ ├── appHeader.spec.js │ ├── appLoad.spec.js │ └── multipleDisplays.spec.js ├── trelliscopeAppFunc.tsx ├── types │ ├── browser-jsonp.d.ts │ ├── configs.d.ts │ ├── global.d.ts │ ├── types.d.ts │ └── typesOld.d.ts └── utils.ts ├── tests ├── columns.spec.ts ├── filters.spec.ts ├── header.spec.ts ├── labels.spec.ts ├── layoutselection.spec.ts ├── pagination.spec.ts ├── paneldialog.spec.ts ├── sorting.spec.ts ├── table.spec.ts └── views.spec.ts ├── tsconfig.json ├── vite-env.d.ts ├── vite.config.ts └── vite.lib.config.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | /.pnp 2 | .pnp.js 3 | 4 | # testing 5 | /coverage 6 | 7 | # production 8 | /build 9 | 10 | # misc 11 | .DS_Store 12 | .env.local 13 | .env.development.local 14 | .env.test.local 15 | .env.production.local 16 | 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | 21 | dist 22 | _ignore 23 | public/_test/ 24 | *.code-workspace 25 | .history -------------------------------------------------------------------------------- /.github/workflows/playwright.yml: -------------------------------------------------------------------------------- 1 | name: Playwright Tests 2 | on: 3 | push: 4 | branches: [ main, master, develop ] 5 | pull_request: 6 | branches: [ main, master, develop ] 7 | jobs: 8 | test: 9 | timeout-minutes: 60 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: actions/setup-node@v3 14 | with: 15 | node-version: 20 16 | - name: Install dependencies 17 | run: npm ci 18 | - name: Install Playwright Browsers 19 | run: npx playwright install --with-deps 20 | - name: Run Playwright tests 21 | run: npx playwright test --project=chromium 22 | - uses: actions/upload-artifact@v3 23 | if: always() 24 | with: 25 | name: playwright-report 26 | path: playwright-report/ 27 | retention-days: 30 28 | -------------------------------------------------------------------------------- /.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 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | _examples/* 22 | !_examples/gapminder_testing/* 23 | !_examples/gapminder_js/gapminder.json 24 | 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | 29 | dist 30 | _ignore 31 | public/_test/ 32 | public/info.html 33 | *.code-workspace 34 | .history 35 | /test-results/ 36 | /playwright-report/ 37 | /playwright/.cache/ 38 | /test-results/ 39 | /playwright-report/ 40 | /blob-report/ 41 | /playwright/.cache/ 42 | -------------------------------------------------------------------------------- /.hygen.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | templates: `${__dirname}/.hygen`, 3 | }; 4 | -------------------------------------------------------------------------------- /.hygen/new/component/component.tsx.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/components/<%= name %>/<%= name %>.tsx 3 | --- 4 | import React from 'react'; 5 | import ErrorWrapper from '../ErrorWrapper'; 6 | import styles from './<%= name %>.module.scss'; 7 | 8 | interface <%= name %>Props {}; 9 | 10 | const <%= name %>: React.FC<<%= name %>Props> = () => { 11 | return ( 12 | 13 |
}> 14 | <%= name %> 15 |
16 |
17 | ); 18 | }; 19 | 20 | export default <%= name %>; 21 | -------------------------------------------------------------------------------- /.hygen/new/component/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | prompt: ({ inquirer }) => { 3 | const questions = [ 4 | { 5 | type: 'input', 6 | name: 'name', 7 | message: 'What is the component name?', 8 | }, 9 | { 10 | type: 'confirm', 11 | name: 'test', 12 | message: 'Will this component have playwright tests?', 13 | }, 14 | ]; 15 | 16 | return inquirer.prompt(questions).then((answers) => { 17 | const { name } = answers; 18 | answers.name = name.charAt(0).toUpperCase() + name.slice(1); 19 | return answers; 20 | }); 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /.hygen/new/component/index.tsx.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/components/<%= name %>/index.ts 3 | --- 4 | export { default } from './<%= name %>'; 5 | -------------------------------------------------------------------------------- /.hygen/new/component/styles.scss.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/components/<%= name %>/<%= name %>.module.scss 3 | --- 4 | .<%= h.changeCase.lcFirst(name) %> {} 5 | -------------------------------------------------------------------------------- /.hygen/new/component/test.js.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: "<%= test ? `tests/${name}.spec.ts` : null %>" 3 | --- 4 | import { test, expect } from '@playwright/test'; 5 | 6 | test.beforeEach(async ({ page }) => { 7 | await page.goto('./'); 8 | }); 9 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v20.11 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /.pnp 2 | .pnp.js 3 | 4 | # testing 5 | /coverage 6 | 7 | # production 8 | /build 9 | 10 | # misc 11 | .DS_Store 12 | .env.local 13 | .env.development.local 14 | .env.test.local 15 | .env.production.local 16 | 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | 21 | dist 22 | _ignore 23 | public/_test/ 24 | *.code-workspace 25 | .history -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 125, 4 | "trailingComma": "all" 5 | } 6 | -------------------------------------------------------------------------------- /.unimportedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignoreUnresolved": [], 3 | "ignoreUnused": [ 4 | "msw" 5 | ], 6 | "ignoreUnimported": [ 7 | "src/setupTests.js", 8 | "src/test-utils.js", 9 | "src/test/__mockData__/mockFunctions.js", 10 | "src/test/__mockData__/restHandlers.js", 11 | "src/test/__mockData__/server.js", 12 | "src/test/__mockData__/worker.js" 13 | ] 14 | } -------------------------------------------------------------------------------- /_examples/gapminder_js/id: -------------------------------------------------------------------------------- 1 | gapminder_js -------------------------------------------------------------------------------- /_examples/gapminder_testing/config.jsonp: -------------------------------------------------------------------------------- 1 | __loadAppConfig__cc1e6a06({ 2 | "name": "Trelliscope App", 3 | "datatype": "jsonp", 4 | "id": "cc1e6a06" 5 | }) -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Afghanistan.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Albania.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Algeria.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Angola.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Argentina.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Australia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Austria.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Bahrain.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Bangladesh.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Belgium.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Benin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Bolivia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Botswana.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Brazil.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Bulgaria.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Burundi.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Cambodia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Cameroon.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Canada.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Chad.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Chile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/China.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Colombia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Comoros.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Congo__Rep_.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Costa_Rica.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Croatia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Cuba.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Denmark.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Djibouti.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Ecuador.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Egypt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Eritrea.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Ethiopia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Finland.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/France.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Gabon.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Gambia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Germany.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Ghana.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Greece.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Guinea.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Haiti.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Honduras.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Hungary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Iceland.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/India.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Iran.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Iraq.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Ireland.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Israel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Italy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Jamaica.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Japan.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Jordan.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Kenya.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Kuwait.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Lebanon.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Lesotho.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Liberia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Libya.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Malawi.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Malaysia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Mali.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Mexico.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Mongolia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Morocco.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Myanmar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Namibia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Nepal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Niger.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Nigeria.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Norway.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Oman.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Pakistan.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Panama.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Paraguay.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Peru.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Poland.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Portugal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Reunion.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Romania.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Rwanda.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Senegal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Serbia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Slovenia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Somalia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Spain.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Sudan.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Sweden.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Syria.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Taiwan.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Tanzania.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Thailand.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Togo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Tunisia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Turkey.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Uganda.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Uruguay.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Vietnam.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Zambia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy/panels/html_panel/Zimbabwe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | mustachewidget 11 |
12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy_by_continent/metaData.js: -------------------------------------------------------------------------------- 1 | window.metaData = [{"continent":1,"mean_lexp":48.8653301282051,"mean_gdp":2193.75457828574,"lexp_time":"panels/lexp_time/Africa.svg"},{"continent":2,"mean_lexp":64.6587366666667,"mean_gdp":7136.11035559,"lexp_time":"panels/lexp_time/Americas.svg"},{"continent":3,"mean_lexp":60.0649032323232,"mean_gdp":7902.15042805404,"lexp_time":"panels/lexp_time/Asia.svg"},{"continent":4,"mean_lexp":71.9036861111111,"mean_gdp":14469.4755333022,"lexp_time":"panels/lexp_time/Europe.svg"},{"continent":5,"mean_lexp":74.3262083333333,"mean_gdp":18621.6092233333,"lexp_time":"panels/lexp_time/Oceania.svg"}] -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy_by_continent/panels/lexp_time/Africa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trelliscope/trelliscopejs-lib/c636be70a87800739811bdbd1b6d8baef8a848ae/_examples/gapminder_testing/displays/Life_expectancy_by_continent/panels/lexp_time/Africa.png -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy_by_continent/panels/lexp_time/Americas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trelliscope/trelliscopejs-lib/c636be70a87800739811bdbd1b6d8baef8a848ae/_examples/gapminder_testing/displays/Life_expectancy_by_continent/panels/lexp_time/Americas.png -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy_by_continent/panels/lexp_time/Asia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trelliscope/trelliscopejs-lib/c636be70a87800739811bdbd1b6d8baef8a848ae/_examples/gapminder_testing/displays/Life_expectancy_by_continent/panels/lexp_time/Asia.png -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy_by_continent/panels/lexp_time/Europe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trelliscope/trelliscopejs-lib/c636be70a87800739811bdbd1b6d8baef8a848ae/_examples/gapminder_testing/displays/Life_expectancy_by_continent/panels/lexp_time/Europe.png -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/Life_expectancy_by_continent/panels/lexp_time/Oceania.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trelliscope/trelliscopejs-lib/c636be70a87800739811bdbd1b6d8baef8a848ae/_examples/gapminder_testing/displays/Life_expectancy_by_continent/panels/lexp_time/Oceania.png -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/displayList.jsonp: -------------------------------------------------------------------------------- 1 | __loadDisplayList__cc1e6a06([ 2 | { 3 | "name": "Life expectancy", 4 | "description": "Life expectancy over time by country", 5 | "tags": [], 6 | "thumbnailurl": "panels/lexp_time/Afghanistan_Asia.svg", 7 | "order": 0 8 | }, 9 | { 10 | "name": "Life expectancy by continent", 11 | "description": "Life expectancy over time by continent", 12 | "tags": [], 13 | "thumbnailurl": "panels/lexp_time/Africa.svg", 14 | "order": 0 15 | } 16 | ]) -------------------------------------------------------------------------------- /_examples/gapminder_testing/displays/libs/mustachewidget/mustachewidget-binding-0.0.0.9000/mustachewidget.js: -------------------------------------------------------------------------------- 1 | HTMLWidgets.widget({ 2 | name: 'mustachewidget', 3 | type: 'output', 4 | factory: function(el, width, height) { 5 | return { 6 | renderValue: function(x) { 7 | if (x.data.pre_render) { 8 | x.data.pre_render(x.data, width, height); 9 | } 10 | var output = Mustache.render(x.template, x.data); 11 | el.innerHTML = output; 12 | }, 13 | resize: function(width, height) { 14 | } 15 | }; 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /_examples/gapminder_testing/id: -------------------------------------------------------------------------------- 1 | cc1e6a06 -------------------------------------------------------------------------------- /_examples/gapminder_testing/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /cors.js: -------------------------------------------------------------------------------- 1 | // Listen on a specific host via the HOST environment variable 2 | var host = process.env.HOST || '0.0.0.0'; 3 | // Listen on a specific port via the PORT environment variable 4 | var port = process.env.PORT || 8080; 5 | 6 | var cors_proxy = require('cors-anywhere'); 7 | cors_proxy 8 | .createServer({ 9 | originWhitelist: [], // Allow all origins 10 | // requireHeader: ['origin', 'x-requested-with'], 11 | // removeHeaders: ['cookie', 'cookie2'] 12 | }) 13 | .listen(port, host, function () { 14 | console.log('Running CORS Anywhere on ' + host + ':' + port); 15 | }); 16 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | PACKAGE_VERSION=$(cat package.json \ 2 | | grep version \ 3 | | head -1 \ 4 | | awk -F: '{ print $2 }' \ 5 | | sed 's/[",]//g') 6 | 7 | rm -rf ../trelliscope/inst/htmlwidgets/lib/trs 8 | mkdir ../trelliscope/inst/htmlwidgets/lib/trs 9 | cp dist/assets/* ../trelliscope/inst/htmlwidgets/lib/trs/ 10 | 11 | cat <../trelliscope/inst/htmlwidgets/trs.yaml 12 | dependencies: 13 | - name: trs 14 | version: $PACKAGE_VERSION 15 | src: htmlwidgets/lib/trs 16 | script: [ index.js ] 17 | stylesheet: [ index.css ] 18 | EOF 19 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | Trelliscope 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trelliscope/trelliscopejs-lib/c636be70a87800739811bdbd1b6d8baef8a848ae/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | Trelliscope 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Trelliscope", 3 | "name": "Trelliscope Viewer", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/TrelliscopeApp.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Provider } from 'react-redux'; 3 | import App from './App'; 4 | import CrossfilterClient from './CrossfilterClient'; 5 | import type { IDataClient } from './DataClient'; 6 | import { prepareTrelliscope } from './jsApi'; 7 | import store from './store'; 8 | 9 | interface TrelliscopeAppProps { 10 | data: ITrelliscopeAppSpec; 11 | width: number; 12 | height: number; 13 | options: { logger?: boolean; mockData?: boolean }; 14 | } 15 | 16 | // component for embedding a Trelliscope app in a React app 17 | const TrelliscopeApp: React.FC = ({ data, width, height, options = {} }) => { 18 | const crossFilterClient = new CrossfilterClient(); 19 | const id = 'trelliscope_app'; 20 | const appDims = { width, height }; 21 | 22 | return ( 23 |
24 | 25 | 33 | 34 |
35 | ); 36 | }; 37 | 38 | export default TrelliscopeApp; 39 | -------------------------------------------------------------------------------- /src/classManipulation.ts: -------------------------------------------------------------------------------- 1 | export const hasClass = (el: HTMLElement, className: string) => { 2 | if (el.classList) { 3 | return el.classList.contains(className); 4 | } 5 | return !!el.className.match(new RegExp(`'(\\s|^)'${className}'(\\s|$)'`)); 6 | }; 7 | 8 | export const addClass = (el: HTMLElement, className: string) => { 9 | if (el.classList) { 10 | el.classList.add(className); 11 | } else if (!hasClass(el, className)) { 12 | el.className += ` ${className}`; // eslint-disable-line no-param-reassign 13 | } 14 | }; 15 | 16 | export const removeClass = (el: HTMLElement, className: string) => { 17 | if (el.classList) { 18 | el.classList.remove(className); 19 | } else if (hasClass(el, className)) { 20 | const reg = new RegExp(`'(\\s|^)'${className}'(\\s|$)'`); 21 | el.className = el.className.replace(reg, ' '); // eslint-disable-line no-param-reassign 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/components/AddViewModal/AddViewModal.module.scss: -------------------------------------------------------------------------------- 1 | .addViewModal {} 2 | -------------------------------------------------------------------------------- /src/components/AddViewModal/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './AddViewModal'; 2 | -------------------------------------------------------------------------------- /src/components/CatHistogram/CatHistogram.module.scss: -------------------------------------------------------------------------------- 1 | .catHistogram { 2 | padding: var(--padding-px) 0; 3 | 4 | &BarWrapper { 5 | position: relative; 6 | font-size: 14px; 7 | margin-bottom: 1px; 8 | border: none; 9 | background: none; 10 | overflow-x: hidden; 11 | width: 100%; 12 | 13 | &:hover { 14 | .catHistogramBarValue { 15 | opacity: 1; 16 | } 17 | } 18 | } 19 | 20 | &Bar { 21 | position: absolute; 22 | top: 0; 23 | left: 0; 24 | display: flex; 25 | align-items: center; 26 | 27 | &Label { 28 | padding-left: var(--margin-3); 29 | line-height: 14px; 30 | cursor: default; 31 | white-space: nowrap; 32 | } 33 | 34 | &Value { 35 | position: absolute; 36 | right: var(--margin-3); 37 | top: 5px; 38 | line-height: 15px; 39 | font-size: var(--text-base); 40 | opacity: 0; 41 | padding-right: 2px; 42 | } 43 | 44 | &Indicator { 45 | position: absolute; 46 | width: 3px; 47 | height: 24px; 48 | top: 0; 49 | right: 0; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/components/CatHistogram/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './CatHistogram'; 2 | -------------------------------------------------------------------------------- /src/components/Chip/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Chip'; 2 | -------------------------------------------------------------------------------- /src/components/ColumnSelector/ColumnSelector.module.scss: -------------------------------------------------------------------------------- 1 | .columnSelector { 2 | &Text { 3 | margin-right: var(--padding-1); 4 | font-size: 15px; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/components/ColumnSelector/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ColumnSelector'; 2 | -------------------------------------------------------------------------------- /src/components/ComposeEmail/ComposeEmail.module.scss: -------------------------------------------------------------------------------- 1 | .composeEmail { 2 | &Container &ContentText { 3 | margin: var(--margin-10) 0; 4 | } 5 | 6 | &Button { 7 | margin: var(--margin-6) !important; 8 | } 9 | 10 | &Description { 11 | display: inline-block; 12 | margin-top: 0; 13 | margin-bottom: var(--margin-10); 14 | } 15 | 16 | &Wrapper { 17 | &Center { 18 | text-align: center; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/components/ComposeEmail/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ComposeEmail'; 2 | -------------------------------------------------------------------------------- /src/components/ConfirmationModal/ConfirmationModal.module.scss: -------------------------------------------------------------------------------- 1 | .confirmationModal {} 2 | -------------------------------------------------------------------------------- /src/components/ConfirmationModal/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ConfirmationModal'; 2 | -------------------------------------------------------------------------------- /src/components/Content/Content.module.scss: -------------------------------------------------------------------------------- 1 | .content { 2 | padding: var(--panelGridGap); 3 | display: grid; 4 | // grid-template-columns: repeat(6, 1fr); 5 | grid-gap: var(--panelGridGap); 6 | width: 100%; 7 | 8 | &Wrapper { 9 | display: flex; 10 | width: 100%; 11 | align-items: flex-start; 12 | justify-content: center; 13 | height: calc(100% - 46px); // TODO: replace with variables 14 | overflow: hidden; 15 | } 16 | } 17 | 18 | .tableContainer { 19 | height: 100%; 20 | } 21 | -------------------------------------------------------------------------------- /src/components/Content/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Content'; 2 | -------------------------------------------------------------------------------- /src/components/ContentContainer/ContentContainer.module.scss: -------------------------------------------------------------------------------- 1 | .contentContainer { 2 | margin-top: 54px; 3 | overflow: hidden; 4 | height: calc(100% - 58px); // TODO: replace with variables 5 | padding: 0; 6 | width: 100%; 7 | // add variable for margin-left 8 | margin-left: 0; 9 | // add variable for transition 10 | transition: margin-left 225ms cubic-bezier(0, 0, 0.2, 1) 0ms; 11 | 12 | &__closed { 13 | margin-left: -400px; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/components/ContentContainer/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ContentContainer'; 2 | -------------------------------------------------------------------------------- /src/components/ContentHeader/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ContentHeader'; 2 | -------------------------------------------------------------------------------- /src/components/Credits/Credits.module.scss: -------------------------------------------------------------------------------- 1 | .credits { 2 | font-size: var(--text-3xl); 3 | padding-left: var(--padding-7); 4 | padding-right: var(--padding-7); 5 | z-index: 5000; 6 | margin-top: 15px; 7 | } 8 | -------------------------------------------------------------------------------- /src/components/Credits/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Credits'; 2 | -------------------------------------------------------------------------------- /src/components/DataTable/DataTable.module.scss: -------------------------------------------------------------------------------- 1 | .dataTable { 2 | table, 3 | td, 4 | tr, 5 | th, 6 | thead, 7 | tbody { 8 | font-family: 'Source Code Pro', monospace; 9 | } 10 | 11 | th { 12 | font-weight: 600; 13 | } 14 | 15 | &CellNumber { 16 | display: flex; 17 | justify-content: flex-end; 18 | } 19 | 20 | &PanelGraphic { 21 | display: flex; 22 | margin: -5px; 23 | position: relative; 24 | 25 | &Expand { 26 | display: none; 27 | position: absolute; 28 | right: 0; 29 | top: 0; 30 | } 31 | 32 | iframe { 33 | border: none; 34 | } 35 | } 36 | 37 | tr { 38 | &:hover .dataTablePanelGraphic { 39 | &Expand { 40 | display: block; 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/components/DataTable/DataTable.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/no-unstable-nested-components */ 2 | import React from 'react'; 3 | 4 | import { MaterialReactTable } from 'material-react-table'; 5 | import styles from './DataTable.module.scss'; 6 | 7 | interface DataTableProps { 8 | table: any; 9 | } 10 | 11 | const DataTable: React.FC = ({ table }) => ( 12 |
13 | 14 |
15 | ); 16 | 17 | export default DataTable; 18 | -------------------------------------------------------------------------------- /src/components/DataTable/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './DataTable'; 2 | -------------------------------------------------------------------------------- /src/components/DisplayInfo/DisplayInfo.module.scss: -------------------------------------------------------------------------------- 1 | .displayInfo { 2 | &Icon { 3 | display: flex; 4 | justify-content: center; 5 | } 6 | 7 | &ModalContainer { 8 | overflow-y: auto; 9 | max-height: var(--dispayInfo-container-max-height); 10 | 11 | iframe { 12 | border: none; 13 | } 14 | } 15 | 16 | &ButtonLeft { 17 | left: 0; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/components/DisplayInfo/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './DisplayInfo'; 2 | -------------------------------------------------------------------------------- /src/components/DisplaySelect/DisplaySelect.module.scss: -------------------------------------------------------------------------------- 1 | .displaySelect { 2 | } 3 | -------------------------------------------------------------------------------- /src/components/DisplaySelect/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './DisplaySelect'; 2 | -------------------------------------------------------------------------------- /src/components/DownloadCsv/DownloadCsv.module.scss: -------------------------------------------------------------------------------- 1 | .downloadCsv { 2 | &Container &ContentText { 3 | margin: var(--margin-10) 0; 4 | } 5 | 6 | &Button { 7 | margin: var(--margin-6) !important; 8 | } 9 | 10 | &Description { 11 | display: inline-block; 12 | margin-top: 0; 13 | margin-bottom: var(--margin-10); 14 | } 15 | 16 | &Wrapper { 17 | &Center { 18 | text-align: center; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/components/DownloadCsv/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './DownloadCsv'; 2 | -------------------------------------------------------------------------------- /src/components/Ellipsis/Ellipsis.module.scss: -------------------------------------------------------------------------------- 1 | .ellipsis {} 2 | -------------------------------------------------------------------------------- /src/components/Ellipsis/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Ellipsis'; 2 | -------------------------------------------------------------------------------- /src/components/ErrorSnack/ErrorSnack.module.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trelliscope/trelliscopejs-lib/c636be70a87800739811bdbd1b6d8baef8a848ae/src/components/ErrorSnack/ErrorSnack.module.scss -------------------------------------------------------------------------------- /src/components/ErrorSnack/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ErrorSnack'; 2 | -------------------------------------------------------------------------------- /src/components/ErrorWrapper/ErrorWrapper.module.scss: -------------------------------------------------------------------------------- 1 | .errorWrapper {} 2 | -------------------------------------------------------------------------------- /src/components/ErrorWrapper/ErrorWrapper.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { ErrorBoundary } from 'react-error-boundary'; 3 | import { useDispatch, useSelector } from 'react-redux'; 4 | import ErrorSnack from '../ErrorSnack'; 5 | // import styles from './ErrorWrapper.module.scss'; 6 | import { selectErrorMessage } from '../../selectors/app'; 7 | import { setErrorMessage } from '../../slices/appSlice'; 8 | 9 | interface ErrorWrapperProps { 10 | children: React.ReactNode; 11 | } 12 | 13 | const ErrorWrapper: React.FC = ({ children }) => { 14 | const dispatch = useDispatch(); 15 | const errorMsg = useSelector(selectErrorMessage); 16 | const [errorInfo, setErrorInfo] = useState(''); 17 | 18 | const handleClose = () => { 19 | dispatch(setErrorMessage('')); 20 | }; 21 | const handleError = (error: Error, info: { componentStack?: string | null | undefined }) => { 22 | setErrorInfo(info?.componentStack || ''); 23 | dispatch(setErrorMessage(error.message)); 24 | }; 25 | 26 | return ( 27 | } 29 | onError={handleError} 30 | > 31 | {children} 32 | 33 | ); 34 | }; 35 | 36 | export default ErrorWrapper; 37 | -------------------------------------------------------------------------------- /src/components/ErrorWrapper/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ErrorWrapper'; 2 | -------------------------------------------------------------------------------- /src/components/ExportInputDialog/ExportInputDialog.module.scss: -------------------------------------------------------------------------------- 1 | .exportInputDialog { 2 | &TextField { 3 | margin-bottom: var(--margin-9) !important; 4 | } 5 | 6 | &Description { 7 | margin-top: 0; 8 | } 9 | 10 | &Container &ContentText { 11 | margin: var(--margin-10) 0; 12 | } 13 | 14 | &ControlsContainer { 15 | display: flex; 16 | justify-content: space-between; 17 | margin: 15px; 18 | 19 | &Stepper { 20 | button { 21 | margin: 0 10px; 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/components/ExportInputDialog/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ExportInputDialog'; -------------------------------------------------------------------------------- /src/components/ExportViewsModal/ExportViewsModal.module.scss: -------------------------------------------------------------------------------- 1 | .exportViewsModal {} 2 | -------------------------------------------------------------------------------- /src/components/ExportViewsModal/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ExportViewsModal'; 2 | -------------------------------------------------------------------------------- /src/components/FilterCat/FilterCat.module.scss: -------------------------------------------------------------------------------- 1 | .filterCat { 2 | &InputContainer { 3 | display: flex; 4 | align-items: center; 5 | justify-content: space-between; 6 | padding-top: var(--padding-3); 7 | padding-bottom: var(--padding-3); 8 | } 9 | 10 | &Regex { 11 | width: 360px; 12 | margin-top: var(--neg-margin-6); 13 | font-size: var(--text-xl); 14 | transform: scale(0.85); 15 | transform-origin: 0 0; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/components/FilterCat/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './FilterCat'; 2 | -------------------------------------------------------------------------------- /src/components/FilterDateRange/FilterDateRange.module.scss: -------------------------------------------------------------------------------- 1 | .filterDateRange { 2 | display: flex; 3 | justify-content: center; 4 | } 5 | -------------------------------------------------------------------------------- /src/components/FilterDateRange/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './FilterDateRange'; 2 | -------------------------------------------------------------------------------- /src/components/FilterDateTimeRange/FilterDateTimeRange.module.scss: -------------------------------------------------------------------------------- 1 | .filterDateTimeRange { 2 | display: flex; 3 | justify-content: center; 4 | } 5 | -------------------------------------------------------------------------------- /src/components/FilterDateTimeRange/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './FilterDateTimeRange'; 2 | -------------------------------------------------------------------------------- /src/components/FilterInput/FilterInput.module.scss: -------------------------------------------------------------------------------- 1 | .filterInput { 2 | padding: var(--padding-px); 3 | position: relative; 4 | 5 | &Header { 6 | display: flex; 7 | justify-content: space-between; 8 | align-items: center; 9 | font-size: var(--text-sm); 10 | 11 | svg { 12 | width: 20px; 13 | font-size: 20px; 14 | } 15 | 16 | &Name { 17 | padding: var(--padding-px) var(--padding-3); 18 | font-weight: 700; 19 | font-size: 16px; 20 | line-height: 1.2; 21 | } 22 | &Label { 23 | padding: var(--padding-px) var(--padding-3); 24 | font-size: 14px; 25 | font-style: italic; 26 | } 27 | 28 | &Controls { 29 | display: flex; 30 | align-items: center; 31 | 32 | &Icon { 33 | cursor: move; 34 | } 35 | } 36 | } 37 | 38 | &SubMenu { 39 | display: flex; 40 | justify-content: space-between; 41 | padding: var(--padding-px) var(--padding-3); 42 | align-items: center; 43 | } 44 | 45 | &Count { 46 | font-size: var(--text-xs); 47 | margin: 0 var(--margin-6) 0 var(--margin-3); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/components/FilterInput/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './FilterInput'; 2 | -------------------------------------------------------------------------------- /src/components/FilterNum/FilterNum.module.scss: -------------------------------------------------------------------------------- 1 | .filterNum { 2 | &Container { 3 | margin-left: var(--margin-4); 4 | margin-right: var(--margin-4); 5 | margin-top: var(--margin-4); 6 | } 7 | 8 | &InputContainer { 9 | width: var(--filter-width); 10 | margin-bottom: var(--neg-margin-8); 11 | padding-bottom: var(--padding-6); 12 | padding-top: var(--padding-2); 13 | z-index: 100; 14 | position: relative; 15 | vertical-align: center; 16 | } 17 | 18 | &RangeInputLabel { 19 | font-size: var(--text-sm); 20 | padding-right: var(--padding-9); 21 | padding-left: var(--padding-3); 22 | display: inline-block; 23 | } 24 | 25 | &RangeInputText { 26 | width: 150px; 27 | margin-top: -5px; 28 | font-size: 16px; 29 | transform: scale(0.85); 30 | transform-origin: 0 0; 31 | } 32 | 33 | &RangeInputTextDash { 34 | transform: scale(2, 1); // to deal with some browsers not being able to handle endash 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/components/FilterNum/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './FilterNum'; 2 | -------------------------------------------------------------------------------- /src/components/Filters/Filters.module.scss: -------------------------------------------------------------------------------- 1 | .filters { 2 | display: flex; 3 | justify-content: center; 4 | position: sticky; 5 | top: 0; 6 | z-index: 1000; 7 | } 8 | -------------------------------------------------------------------------------- /src/components/Filters/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Filters'; 2 | -------------------------------------------------------------------------------- /src/components/FormattedNumber/FormattedNumber.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import '@testing-library/jest-dom'; 4 | import FormattedNumber from './FormattedNumber'; 5 | 6 | describe(`FormattedNumber component`, () => { 7 | 8 | it('should render', () => { 9 | render( 10 | 11 | ); 12 | 13 | expect(screen.getByText('FormattedNumber')).toBeInTheDocument(); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/components/FormattedNumber/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './FormattedNumber'; 2 | -------------------------------------------------------------------------------- /src/components/FullscreenButton/FullscreenButton.module.scss: -------------------------------------------------------------------------------- 1 | .fullscreenButton { 2 | } 3 | -------------------------------------------------------------------------------- /src/components/FullscreenButton/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './FullscreenButton'; -------------------------------------------------------------------------------- /src/components/Header/Header.module.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | display: flex; 3 | justify-content: space-between; 4 | width: 100%; 5 | align-items: center; 6 | 7 | &AppBar { 8 | height: 54px; 9 | } 10 | 11 | &Toolbar { 12 | padding-left: 20px; 13 | min-height: 54px !important; 14 | } 15 | 16 | &Right { 17 | display: flex; 18 | align-items: center; 19 | overflow-x: auto; 20 | overflow-y: hidden; 21 | text-wrap: nowrap; 22 | } 23 | 24 | &IconButton { 25 | margin-right: 20px; 26 | } 27 | 28 | &DisplayInfo { 29 | display: flex; 30 | align-items: center; 31 | 32 | &TitleContainer { 33 | margin-left: 10px; 34 | } 35 | } 36 | 37 | &Trelliscope { 38 | height: 54px; 39 | font-family: 'Jost'; 40 | display: flex; 41 | flex-direction: row; 42 | line-height: 54px; 43 | font-size: 17px; 44 | font-weight: 500; 45 | text-transform: uppercase; 46 | letter-spacing: 1px; 47 | 48 | &Fullscreen { 49 | margin-left: 4px; 50 | padding-left: 7px; 51 | padding-right: 7px; 52 | line-height: 50px; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/components/Header/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Header'; 2 | -------------------------------------------------------------------------------- /src/components/HelpInfo/HelpInfo.module.scss: -------------------------------------------------------------------------------- 1 | .helpInfo { 2 | &Icon { 3 | opacity: 0.7; 4 | } 5 | 6 | &Dialog { 7 | &Website { 8 | font-size: 16px; 9 | font-weight: 300; 10 | opacity: 0.8; 11 | } 12 | 13 | &Headline { 14 | font-size: var(--text-7xl); 15 | padding-top: var(--padding-10); 16 | margin-bottom: var(--margin-8); 17 | font-weight: var(--font-normal); 18 | } 19 | 20 | &Div { 21 | font-size: var(--text-3xl); 22 | padding-left: var(--padding-7); 23 | padding-right: var(--padding-7); 24 | z-index: 5000; 25 | } 26 | 27 | &H4 { 28 | margin-bottom: var(--margin-4); 29 | } 30 | 31 | &Ul { 32 | margin-top: 0; 33 | break-inside: avoid-column; 34 | } 35 | 36 | &P { 37 | text-indent: -20px; 38 | padding-left: var(--padding-11); 39 | margin-top: var(--margin-px); 40 | } 41 | 42 | &P2 { 43 | padding-left: var(--padding-11); 44 | } 45 | 46 | &P22 { 47 | text-indent: 0; 48 | padding-left: var(--padding-11); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/components/HelpInfo/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './HelpInfo'; 2 | -------------------------------------------------------------------------------- /src/components/HowToUse/HowToUse.module.scss: -------------------------------------------------------------------------------- 1 | .howToUse { 2 | font-size: var(--text-3xl); 3 | padding-left: var(--padding-7); 4 | padding-right: var(--padding-7); 5 | z-index: 5000; 6 | 7 | &Icon { 8 | position: absolute; 9 | top: 1px; 10 | right: 1px; 11 | } 12 | 13 | &Headline { 14 | font-size: var(--text-7xl); 15 | padding-top: var(--padding-10); 16 | margin-bottom: var(--margin-8); 17 | font-weight: var(--font-normal); 18 | } 19 | 20 | &H4 { 21 | margin-bottom: var(--margin-4); 22 | } 23 | 24 | &Ul { 25 | margin-top: 0; 26 | break-inside: avoid-column; 27 | } 28 | 29 | &P { 30 | text-indent: -20px; 31 | padding-left: var(--padding-11); 32 | margin-top: var(--margin-px); 33 | } 34 | 35 | &P2 { 36 | padding-left: var(--padding-11); 37 | } 38 | 39 | &P22 { 40 | text-indent: 0; 41 | padding-left: var(--padding-11); 42 | } 43 | 44 | &ContentContainer { 45 | margin-top: var(--margin-px); 46 | display: flex; 47 | justify-content: space-between; 48 | } 49 | 50 | &InlineIcon { 51 | vertical-align: middle; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/components/HowToUse/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './HowToUse'; 2 | -------------------------------------------------------------------------------- /src/components/ImportViewsModal/ImportViewsModal.module.scss: -------------------------------------------------------------------------------- 1 | .impportViewsModal { 2 | } 3 | -------------------------------------------------------------------------------- /src/components/ImportViewsModal/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ImportViewsModal'; 2 | -------------------------------------------------------------------------------- /src/components/Labels/Labels.module.scss: -------------------------------------------------------------------------------- 1 | .labels { 2 | &Container { 3 | display: flex; 4 | align-items: center; 5 | padding-right: 4px; 6 | margin-left: -4px; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/components/Labels/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Labels'; 2 | -------------------------------------------------------------------------------- /src/components/LayoutSelector/LayoutSelector.module.scss: -------------------------------------------------------------------------------- 1 | .layoutSelector { 2 | display: flex; 3 | align-items: center; 4 | 5 | &Text { 6 | margin-right: var(--padding-1); 7 | font-size: 15px; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/LayoutSelector/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './LayoutSelector'; 2 | -------------------------------------------------------------------------------- /src/components/NumHistogram/NumHistogram.module.scss: -------------------------------------------------------------------------------- 1 | .brush { 2 | fill: none; 3 | 4 | &Overlay { 5 | pointer-events: all; 6 | } 7 | 8 | &Selection { 9 | cursor: move; 10 | fill-opacity: 0.125; 11 | shape-rendering: crispEdges; 12 | stroke-opacity: 0.2; 13 | } 14 | 15 | &Handle { 16 | width: 6px; 17 | cursor: ew-resize; 18 | } 19 | } 20 | 21 | .axis { 22 | font-size: 10px; 23 | opacity: 0.4; 24 | shape-rendering: crispEdges; 25 | 26 | &Tick { 27 | &Text { 28 | text-anchor: middle; 29 | } 30 | 31 | &Super { 32 | font-size: 8px; 33 | text-anchor: middle; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/components/NumHistogram/NumHistogramBar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useTheme } from '@mui/material/styles'; 3 | 4 | interface NumHistogramBarProps { 5 | name: string; 6 | x: number; 7 | y: number; 8 | width: number; 9 | height: number; 10 | active: boolean; 11 | } 12 | 13 | const NumHistogramBar: React.FC = ({ name, x, y, width, height, active }) => { 14 | const theme = useTheme(); 15 | return ( 16 | <> 17 | 28 | 40 | 41 | ); 42 | }; 43 | 44 | export default NumHistogramBar; 45 | -------------------------------------------------------------------------------- /src/components/NumHistogram/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './NumHistogram'; 2 | -------------------------------------------------------------------------------- /src/components/Pagination/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Pagination'; -------------------------------------------------------------------------------- /src/components/Panel/Panel.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import '@testing-library/jest-dom'; 4 | import PanelNew from './Panel'; 5 | 6 | describe(`PanelNew component`, () => { 7 | it('should render', () => { 8 | render(); 9 | 10 | expect(screen.getByText('PanelNew')).toBeInTheDocument(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/components/Panel/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Panel'; 2 | export { default as PanelGraphic } from './PanelGraphic'; 3 | export { default as PanelGraphicWrapper } from './PanelGraphicWrapper'; 4 | -------------------------------------------------------------------------------- /src/components/PanelDialog/PanelDialog.module.scss: -------------------------------------------------------------------------------- 1 | .panelDialog { 2 | &Graphic { 3 | padding: var(--padding-10); 4 | display: flex; 5 | justify-content: center; 6 | max-height: 500px; 7 | 8 | img { 9 | max-height: 500px; 10 | width: clamp(320px, 100vw, 800px); 11 | max-width: 100%; 12 | } 13 | 14 | iframe { 15 | border: none; 16 | // max-height: 500px; 17 | height: auto; 18 | // max-width: 100%; 19 | // width: min(80vw, 80vh); 20 | // height: min(80vw, 80vh); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/PanelDialog/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './PanelDialog'; 2 | -------------------------------------------------------------------------------- /src/components/PanelInputs/PanelInputs.module.scss: -------------------------------------------------------------------------------- 1 | .panelInputText { 2 | display: flex; 3 | width: calc(100% - 12px); 4 | 5 | &ButtonContainer { 6 | width: inherit; 7 | display: flex; 8 | } 9 | 10 | &Value { 11 | text-overflow: ellipsis; 12 | overflow: hidden; 13 | white-space: nowrap; 14 | margin-right: 5px; 15 | 16 | // hack to remove default safari tooltip 17 | &::after { 18 | content: ''; 19 | display: block; 20 | } 21 | } 22 | 23 | &EditButton { 24 | cursor: pointer; 25 | border: none; 26 | background: none; 27 | padding: 0; 28 | margin: 0; 29 | opacity: 0.5; 30 | } 31 | 32 | &Popover { 33 | padding: var(--padding-9); 34 | } 35 | } 36 | 37 | .panelInputRadioGroup { 38 | &Label { 39 | font-size: var(--text-sm) !important; 40 | } 41 | 42 | &Radio { 43 | padding: var(--padding-2) var(--padding-5) !important; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/components/PanelInputs/index.ts: -------------------------------------------------------------------------------- 1 | export { default as PanelInputText } from './PanelInputText'; 2 | export { default as PanelInputRadios } from './PanelInputRadios'; 3 | export { default as PanelInputSelect } from './PanelInputSelect'; 4 | export { default as PanelInputMultiSelect } from './PanelInputMultiSelect'; 5 | export { default as PanelInputCheckbox } from './PanelInputCheckbox'; 6 | -------------------------------------------------------------------------------- /src/components/PanelLabels/PanelLabels.module.scss: -------------------------------------------------------------------------------- 1 | .panelLabels { 2 | width: 100%; 3 | list-style-type: none; 4 | margin: 0; 5 | border-collapse: collapse; 6 | table-layout: fixed; 7 | 8 | &Row { 9 | width: 100%; 10 | 11 | &:last-of-type { 12 | border-bottom: none; 13 | } 14 | } 15 | 16 | &Cell { 17 | text-overflow: ellipsis; 18 | overflow: hidden; 19 | white-space: nowrap; 20 | width: 70%; 21 | 22 | &:first-of-type { 23 | white-space: nowrap; 24 | font-weight: bold; 25 | cursor: default; 26 | width: 30%; 27 | } 28 | 29 | &Content { 30 | display: flex; 31 | justify-content: space-between; 32 | 33 | &Text { 34 | text-overflow: ellipsis; 35 | overflow: hidden; 36 | white-space: nowrap; 37 | // hack to remove default safari tooltip 38 | &::after { 39 | content: ''; 40 | display: block; 41 | } 42 | } 43 | } 44 | } 45 | 46 | &Close { 47 | cursor: pointer; 48 | border: none; 49 | background: none; 50 | padding: 0; 51 | margin: 0; 52 | opacity: 0.5; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/components/PanelLabels/PanelLabelsCell.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box, Tooltip } from '@mui/material'; 3 | import { useTheme } from '@mui/material/styles'; 4 | import styles from './PanelLabels.module.scss'; 5 | 6 | interface PanelLabelsCellProps { 7 | value: string | number | null; 8 | label: string | null; 9 | padding: number | null; 10 | } 11 | 12 | const PanelLabelsCell: React.FC = ({ value, label, padding }) => { 13 | const theme = useTheme(); 14 | return ( 15 | 19 | {label ? ( 20 | 21 | 22 | {value} 23 | 24 | 25 | ) : ( 26 | {value} 27 | )} 28 | 29 | ); 30 | }; 31 | 32 | export default PanelLabelsCell; 33 | -------------------------------------------------------------------------------- /src/components/PanelLabels/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './PanelLabels'; 2 | -------------------------------------------------------------------------------- /src/components/PanelPicker/PanelPicker.module.scss: -------------------------------------------------------------------------------- 1 | .panelPicker {} 2 | -------------------------------------------------------------------------------- /src/components/PanelPicker/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './PanelPicker'; 2 | -------------------------------------------------------------------------------- /src/components/PanelZoomLabels/PanelZoomLabelsCell.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Tooltip } from '@mui/material'; 2 | import React from 'react'; 3 | import { useTheme } from '@mui/material/styles'; 4 | import styles from './PanelZoomLabels.module.scss'; 5 | 6 | interface PanelZoomLabelsCellProps { 7 | value: string | number | null; 8 | label: string | null; 9 | } 10 | 11 | const PanelZoomLabelsCell: React.FC = ({ value, label }) => { 12 | const theme = useTheme(); 13 | return ( 14 | 22 | {label ? ( 23 | 24 | 25 | {value} 26 | {label !== value && {label}} 27 | 28 | 29 | ) : ( 30 | {value} 31 | )} 32 | 33 | ); 34 | }; 35 | 36 | export default PanelZoomLabelsCell; 37 | -------------------------------------------------------------------------------- /src/components/PanelZoomLabels/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './PanelZoomLabels'; 2 | -------------------------------------------------------------------------------- /src/components/Share/Share.module.scss: -------------------------------------------------------------------------------- 1 | .share { 2 | &Content { 3 | display: flex; 4 | align-items: center; 5 | width: 650px; 6 | 7 | &Text { 8 | margin-right: 10px !important; 9 | 10 | input { 11 | cursor: pointer; 12 | } 13 | } 14 | } 15 | &Button { 16 | width: 90px; 17 | 18 | &Icon { 19 | margin-right: 10px; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/components/Share/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Share'; 2 | -------------------------------------------------------------------------------- /src/components/Shortcuts/Shortcuts.module.scss: -------------------------------------------------------------------------------- 1 | .shortcuts { 2 | font-size: var(--text-3xl); 3 | padding-left: var(--padding-7); 4 | padding-right: var(--padding-7); 5 | z-index: 5000; 6 | 7 | &Div { 8 | width: 50%; 9 | display: block; 10 | float: left; 11 | } 12 | 13 | &Headline { 14 | font-size: var(--text-7xl); 15 | padding-top: var(--padding-10); 16 | margin-bottom: var(--margin-8); 17 | font-weight: var(--font-normal); 18 | } 19 | 20 | &H4 { 21 | margin-bottom: var(--margin-4); 22 | } 23 | 24 | &Ul { 25 | margin-top: 0; 26 | break-inside: avoid-column; 27 | } 28 | 29 | &P { 30 | text-indent: -20px; 31 | padding-left: var(--padding-11); 32 | margin-top: var(--margin-px); 33 | } 34 | 35 | &P2 { 36 | padding-left: var(--padding-11); 37 | } 38 | 39 | &P22 { 40 | text-indent: 0; 41 | padding-left: var(--padding-11); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/components/Shortcuts/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Shortcuts'; 2 | -------------------------------------------------------------------------------- /src/components/Sidebar/Sidebar.module.scss: -------------------------------------------------------------------------------- 1 | .sidebar { 2 | flex-shrink: 0; 3 | 4 | &DragContainer { 5 | overflow: auto; 6 | } 7 | 8 | &DragSubContainer { 9 | overflow: hidden; 10 | min-height: 600px; 11 | } 12 | 13 | &NoFilter { 14 | margin-left: 5px; 15 | margin-top: 10px; 16 | display: flex; 17 | justify-content: flex-start; 18 | } 19 | 20 | &BobbingArrow { 21 | margin-top: 3px; 22 | margin-left: 5px; 23 | position: relative; 24 | animation: bobbing 1.5s ease-in-out infinite; 25 | } 26 | 27 | @keyframes bobbing { 28 | 0% { 29 | transform: translateY(0); 30 | } 31 | 50% { 32 | transform: translateY(-10px); 33 | } 34 | 100% { 35 | transform: translateY(0); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/Sidebar/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Sidebar'; 2 | -------------------------------------------------------------------------------- /src/components/Sort/Sort.module.scss: -------------------------------------------------------------------------------- 1 | .sort { 2 | &DragContainer { 3 | display: flex; 4 | align-items: center; 5 | overflow: hidden; 6 | } 7 | &Container { 8 | display: flex; 9 | align-items: center; 10 | } 11 | &Text { 12 | font-size: 15px; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/Sort/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Sort'; 2 | -------------------------------------------------------------------------------- /src/components/Tour/Tour.module.scss: -------------------------------------------------------------------------------- 1 | .tour {} 2 | -------------------------------------------------------------------------------- /src/components/Tour/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Tour'; 2 | -------------------------------------------------------------------------------- /src/components/UserInfo/UserInfo.module.scss: -------------------------------------------------------------------------------- 1 | .userInfo { 2 | &Container &ContentText { 3 | margin: var(--margin-10) 0; 4 | } 5 | 6 | &TextField { 7 | margin-bottom: var(--margin-9) !important; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/UserInfo/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './UserInfo'; 2 | -------------------------------------------------------------------------------- /src/components/VariableSelector/VariableSelector.module.scss: -------------------------------------------------------------------------------- 1 | .variableSelector { 2 | &PopupIndicatorOpen { 3 | display: none !important; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/components/VariableSelector/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './VariableSelector'; 2 | -------------------------------------------------------------------------------- /src/components/Views/Views.module.scss: -------------------------------------------------------------------------------- 1 | .views {} 2 | -------------------------------------------------------------------------------- /src/components/Views/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Views'; 2 | -------------------------------------------------------------------------------- /src/getCustomProperties.tsx: -------------------------------------------------------------------------------- 1 | const getCustomProperties = (variables: string[], asNumber = true) => 2 | variables.map((variable) => { 3 | const value = getComputedStyle(document.documentElement).getPropertyValue(variable); 4 | if (asNumber) { 5 | return parseFloat(value) as number; 6 | } 7 | 8 | return value; 9 | }); 10 | 11 | export default getCustomProperties; 12 | -------------------------------------------------------------------------------- /src/middleware/callbackMiddleware.ts: -------------------------------------------------------------------------------- 1 | import { selectCallbacks } from '../selectors'; 2 | import type { RootState } from '../store'; 3 | 4 | interface GetStateProps { 5 | getState: () => RootState; 6 | } 7 | 8 | // eslint-disable-next-line no-unused-vars 9 | const callbackMiddleware = 10 | ({ getState }: GetStateProps) => 11 | (next: (arg0: { type: string }) => void) => 12 | (action: { type: string }) => { 13 | const callbacks = selectCallbacks(getState()); 14 | if (!callbacks) { 15 | return next(action); 16 | } 17 | 18 | const cbKeys = Object.keys(callbacks); 19 | if (callbacks && cbKeys.indexOf(action.type) !== -1) { 20 | callbacks[action.type](); 21 | } 22 | return next(action); 23 | }; 24 | 25 | export default callbackMiddleware; 26 | -------------------------------------------------------------------------------- /src/selectors/app.ts: -------------------------------------------------------------------------------- 1 | import type { RootState } from '../store'; 2 | 3 | export const selectAppId = (state: RootState) => state.app.appId; 4 | export const selectBasePath = (state: RootState) => state.app.basePath; 5 | export const selectConfigPath = (state: RootState) => state.app.configPath; 6 | export const selectAppData = (state: RootState) => state.app.appData; 7 | export const selectDialogOpen = (state: RootState) => state.app.dialog; 8 | export const selectErrorMessage = (state: RootState) => state.app.errorMsg; 9 | export const selectPanelDialog = (state: RootState) => state.app.panelDialog; 10 | export const panelDialogIsOpenSelector = (state: RootState) => state.app.panelDialog.open; 11 | export const selectMetaData = (state: RootState) => state.app.metaData; 12 | export const selectMetaDataState = (state: RootState) => state.app.metaDataState; 13 | -------------------------------------------------------------------------------- /src/selectors/index.ts: -------------------------------------------------------------------------------- 1 | // cross-component selectors go here 2 | // otherwise they go in the component files 3 | import type { RootState } from '../store'; 4 | 5 | export const selectedDisplaySelector = (state: RootState) => state.selectedDisplay; 6 | 7 | export const relDispPositionsSelector = (state: RootState) => state.relDispPositions; 8 | 9 | export const cogDataSelector = (state: RootState) => state.cogDataMutable; 10 | 11 | export const filterSelector = (state: RootState) => state.filter; 12 | export const filterViewSelector = (state: RootState) => state.filter.view; 13 | export const labelsSelector = (state: RootState) => state.labels || []; 14 | 15 | export const appIdSelector = (state: RootState) => state.app.appId; 16 | export const singlePageAppSelector = (state: RootState) => state.app.singlePageApp; 17 | export const fullscreenSelector = (state: RootState) => state.app.fullscreen; 18 | 19 | export const selectCallbacks = (state: RootState) => state?.app.options?.callbacks; 20 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom'; 2 | 3 | import failOnConsole from 'jest-fail-on-console'; 4 | 5 | failOnConsole(); 6 | -------------------------------------------------------------------------------- /src/slices/configSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | import type { PayloadAction } from '@reduxjs/toolkit'; 3 | 4 | export interface ConfigState { 5 | isFetching: boolean; 6 | isLoaded: boolean; 7 | didInvalidate: boolean; 8 | config: IConfig; 9 | lastUpdate?: number; 10 | } 11 | 12 | const initialState: ConfigState = { 13 | isFetching: false, 14 | isLoaded: false, 15 | didInvalidate: false, 16 | config: {} as IConfig, 17 | }; 18 | 19 | export const configState = createSlice({ 20 | name: 'config', 21 | initialState, 22 | reducers: { 23 | requestConfig: (state) => { 24 | state.isFetching = true; 25 | state.isLoaded = false; 26 | state.didInvalidate = false; 27 | }, 28 | receiveConfig: (state, action: PayloadAction) => { 29 | state.isFetching = false; 30 | state.isLoaded = false; 31 | state.didInvalidate = true; 32 | state.config = action.payload; 33 | state.lastUpdate = Date.now(); 34 | }, 35 | }, 36 | }); 37 | 38 | export const { requestConfig, receiveConfig } = configState.actions; 39 | 40 | export default configState.reducer; 41 | -------------------------------------------------------------------------------- /src/slices/htmlAPI.ts: -------------------------------------------------------------------------------- 1 | import { BaseQueryFn, createApi } from '@reduxjs/toolkit/query/react'; 2 | import DOMPurify from 'dompurify'; 3 | 4 | const HTMLBaseQuery = 5 | (): BaseQueryFn<{ 6 | url: string; 7 | }> => 8 | async ({ url }) => { 9 | const response = await fetch(url); 10 | if (!response.ok) { 11 | throw new Error(`Failed to load HTML from ${url}: ${response.status}`); 12 | } 13 | const dirtyData = await response.text(); 14 | const cleanData = DOMPurify.sanitize(dirtyData); 15 | return { data: cleanData }; 16 | }; 17 | 18 | export const htmlAPI = createApi({ 19 | reducerPath: 'html', 20 | baseQuery: HTMLBaseQuery(), 21 | endpoints: (builder) => ({ 22 | getHtml: builder.query({ 23 | query: ({ url }) => ({ url }), 24 | }), 25 | }), 26 | }); 27 | 28 | export const { useGetHtmlQuery } = htmlAPI; 29 | 30 | export const useHtml = (url: string, isCustom: boolean) => useGetHtmlQuery({ url }, { skip: !url || !isCustom }); 31 | -------------------------------------------------------------------------------- /src/slices/labelsSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | import type { PayloadAction } from '@reduxjs/toolkit'; 3 | import type { RootState } from '../store'; 4 | import { displayInfoAPI } from './displayInfoAPI'; 5 | import { selectHash, selectHashLabels } from '../selectors/hash'; 6 | 7 | const initialState: string[] = selectHashLabels(); 8 | 9 | export const labelsSlice = createSlice({ 10 | name: 'labels', 11 | initialState, 12 | reducers: { 13 | setLabels: (state, action: PayloadAction) => action.payload, 14 | }, 15 | extraReducers: (builder) => { 16 | builder.addMatcher(displayInfoAPI.endpoints.getDisplayInfo.matchFulfilled, (state, action) => { 17 | const hash = selectHash(); 18 | const hashLabels = selectHashLabels(); 19 | if (Object.keys(hash).length > 2 && hashLabels.length === 0) { 20 | return []; 21 | } 22 | if (hashLabels.length) { 23 | return hashLabels; 24 | } 25 | const { labels } = action.payload.state; 26 | return labels.varnames; 27 | }); 28 | }, 29 | }); 30 | 31 | export const { setLabels } = labelsSlice.actions; 32 | export const selectLabels = (state: RootState) => state.labels; 33 | 34 | export default labelsSlice.reducer; 35 | -------------------------------------------------------------------------------- /src/slices/relDispPositionsSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | import type { PayloadAction } from '@reduxjs/toolkit'; 3 | 4 | export interface RelDispPositionsState { 5 | aspect: number; 6 | col: number; 7 | height: number; 8 | width: number; 9 | idx: number; 10 | left: number; 11 | name: string; 12 | row: number; 13 | top: number; 14 | } 15 | 16 | const initialState: RelDispPositionsState[] = []; 17 | 18 | export const relDispPositionsSlice = createSlice({ 19 | name: 'relDispPositions', 20 | initialState, 21 | reducers: { 22 | setRelDispPositions: (state, action: PayloadAction) => Object.assign([], [], action.payload), 23 | }, 24 | }); 25 | 26 | export const { setRelDispPositions } = relDispPositionsSlice.actions; 27 | 28 | export default relDispPositionsSlice.reducer; 29 | -------------------------------------------------------------------------------- /src/slices/selectedRelDispsSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | import type { PayloadAction } from '@reduxjs/toolkit'; 3 | 4 | const initialState: number[] = []; 5 | 6 | export const selectedRelDispsSlice = createSlice({ 7 | name: 'selectedRelDisps', 8 | initialState, 9 | reducers: { 10 | setSelectedRelDisps: (state, action: PayloadAction) => { 11 | const newState = Object.assign([] as number[], state); 12 | if (action.payload.length === 0) { 13 | return []; 14 | } 15 | if (action.payload.length > 0) { 16 | return action.payload; 17 | } 18 | newState.sort(); 19 | return newState; 20 | }, 21 | }, 22 | }); 23 | 24 | export const { setSelectedRelDisps } = selectedRelDispsSlice.actions; 25 | 26 | export const selectSelectedRelDisps = (state: { selectedRelDisps: number[] }) => state.selectedRelDisps; 27 | 28 | export default selectedRelDispsSlice.reducer; 29 | -------------------------------------------------------------------------------- /src/slices/uiSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | import type { PayloadAction } from '@reduxjs/toolkit'; 3 | 4 | export interface UIState { 5 | windowHeight: number; 6 | windowWidth: number; 7 | origHeight: number; 8 | origWidth: number; 9 | } 10 | 11 | const initialState: UIState = { 12 | windowHeight: typeof window === 'object' ? window.innerHeight : 0, 13 | windowWidth: typeof window === 'object' ? window.innerWidth : 0, 14 | origHeight: 0, 15 | origWidth: 0, 16 | }; 17 | 18 | export const uiSlice = createSlice({ 19 | name: 'ui', 20 | initialState, 21 | reducers: { 22 | windowResize: (state, action: PayloadAction<{ width: number; height: number }>) => { 23 | state.windowHeight = action.payload.height; 24 | state.windowWidth = action.payload.width; 25 | }, 26 | setAppDims: (state, action: PayloadAction<{ width: number; height: number }>) => { 27 | state.origHeight = action.payload.height; 28 | state.origWidth = action.payload.width; 29 | }, 30 | }, 31 | }); 32 | 33 | export const { windowResize, setAppDims } = uiSlice.actions; 34 | 35 | export default uiSlice.reducer; 36 | -------------------------------------------------------------------------------- /src/store.ts: -------------------------------------------------------------------------------- 1 | import { configureStore } from '@reduxjs/toolkit'; 2 | import callbackMiddleware from './middleware/callbackMiddleware'; 3 | import { hashMiddleware } from './middleware/hash'; 4 | import { configAPI } from './slices/configAPI'; 5 | import { displayListAPI } from './slices/displayListAPI'; 6 | import reducer from './reducers'; 7 | import { displayInfoAPI } from './slices/displayInfoAPI'; 8 | import { htmlAPI } from './slices/htmlAPI'; 9 | 10 | const store = configureStore({ 11 | reducer, 12 | middleware: (getDefaultMiddleware: any) => getDefaultMiddleware().concat( 13 | htmlAPI.middleware, 14 | configAPI.middleware, 15 | displayListAPI.middleware, 16 | displayInfoAPI.middleware, 17 | callbackMiddleware, 18 | hashMiddleware, 19 | ), 20 | devTools: process.env.NODE_ENV !== 'production', 21 | }); 22 | 23 | export default store; 24 | 25 | export type RootState = ReturnType; 26 | 27 | export type AppDispatch = typeof store.dispatch; 28 | -------------------------------------------------------------------------------- /src/test-utils.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { render } from '@testing-library/react'; 4 | import { ThemeProvider, createTheme } from '@mui/material/styles'; 5 | import { Provider } from 'react-redux'; 6 | import store from './store'; 7 | import { setAppID, setFullscreen, setOptions, setSinglePageApp } from './slices/appSlice'; 8 | import { setAppDims } from './slices/uiSlice'; 9 | 10 | const singlePageApp = true; 11 | const fullscreen = true; 12 | const id = 'theelementid'; 13 | const theme = createTheme(); 14 | 15 | store.dispatch(setAppID(id)); 16 | store.dispatch(setOptions({})); 17 | store.dispatch(setFullscreen(fullscreen)); 18 | store.dispatch(setSinglePageApp(singlePageApp)); 19 | store.dispatch(setAppDims({ width: 500, height: 500 })); 20 | 21 | const Providers = ({ children }) => ( 22 | 23 | {children} 24 | 25 | ); 26 | 27 | Providers.propTypes = { 28 | children: PropTypes.element.isRequired, 29 | }; 30 | 31 | const customRender = (ui, options) => render(ui, { wrapper: Providers, ...options }); 32 | 33 | export * from '@testing-library/react'; 34 | 35 | export { customRender as render }; 36 | -------------------------------------------------------------------------------- /src/test/__mockData__/restHandlers.js: -------------------------------------------------------------------------------- 1 | import { rest } from 'msw'; 2 | import { createConfig, createDisplayList, createDisplayObj, createCogData, createPanelData } from './mockFunctions'; 3 | 4 | export default [ 5 | rest.get('/config.json', (req, res, ctx) => res(ctx.json(createConfig()))), 6 | 7 | rest.get('/displays/displayList.json', (req, res, ctx) => res(ctx.json(createDisplayList()))), 8 | 9 | // TODO data create methods should be able to share values that should match (maybe create single class/instance) 10 | rest.get('/displays/common/:displayGroup/displayObj.json', (req, res, ctx) => 11 | res(ctx.json(createDisplayObj({ name: req.params.displayGroup }))), 12 | ), 13 | 14 | rest.get('/displays/common/:displayGroup/cogData.json', (req, res, ctx) => res(ctx.json(createCogData()))), 15 | 16 | rest.get('/displays/common/:displayGroup/json/:fileName', (req, res, ctx) => res(ctx.json(createPanelData()))), 17 | ]; 18 | -------------------------------------------------------------------------------- /src/test/__mockData__/server.js: -------------------------------------------------------------------------------- 1 | import { setupServer } from 'msw/node'; 2 | import restHandlers from './restHandlers'; 3 | 4 | export default setupServer(...restHandlers); 5 | -------------------------------------------------------------------------------- /src/test/__mockData__/worker.js: -------------------------------------------------------------------------------- 1 | import { setupWorker } from 'msw'; 2 | import restHandlers from './restHandlers'; 3 | 4 | export default setupWorker(...restHandlers); 5 | -------------------------------------------------------------------------------- /src/types/browser-jsonp.d.ts: -------------------------------------------------------------------------------- 1 | interface BrowserJSONPOptions { 2 | url: string; 3 | data?: { [key: string]: unknown }; 4 | success?: (data: never) => void; 5 | error?: (err: { url: string, event: ErrorEvent }) => void; 6 | complete?: (data: { url: string, event: Event }) => void; 7 | beforeSend?: (emptyObj: unknown, params: BrowserJSONPOptions) => void; 8 | callbackName?: string; 9 | } 10 | 11 | declare module 'browser-jsonp' { 12 | export default function jsonp(options: BrowserJSONPOptions): void; 13 | } -------------------------------------------------------------------------------- /src/types/types.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trelliscope/trelliscopejs-lib/c636be70a87800739811bdbd1b6d8baef8a848ae/src/types/types.d.ts -------------------------------------------------------------------------------- /src/types/typesOld.d.ts: -------------------------------------------------------------------------------- 1 | interface AppOptions { 2 | logger?: boolean; 3 | mockData?: boolean; 4 | callbacks?: { 5 | [key: string]: () => void; 6 | }; 7 | } 8 | interface Window { 9 | trelliscopeApp: (id: string, config: string, options: AppOptions) => void; 10 | } 11 | -------------------------------------------------------------------------------- /tests/columns.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | test.beforeEach(async ({ page }) => { 4 | await page.goto('./'); 5 | }); 6 | 7 | test('columns can be adjusted', async ({ page }) => { 8 | await page.getByTestId('fullscreen-button').click(); 9 | await expect(page.getByTestId('column-selector-input')).toBeVisible(); 10 | await page.getByTestId('column-selector-input').fill('3'); 11 | await expect(page.getByTestId('column-selector-input')).toHaveValue('3'); 12 | await expect(page.getByTestId('pagination-numbers')).toHaveText('1 - 6 of 31142 total'); 13 | await page.getByTestId('column-selector-input').fill('6'); 14 | await expect(page.getByTestId('column-selector-input')).toHaveValue('6'); 15 | await expect(page.getByTestId('pagination-numbers')).toHaveText('1 - 24 of 31142 total'); 16 | }); 17 | -------------------------------------------------------------------------------- /tests/layoutselection.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | test.beforeEach(async ({ page }) => { 4 | await page.goto('./'); 5 | }); 6 | 7 | test('layout selector is present with options', async ({ page }) => { 8 | await page.getByTestId('layout-selector').click(); 9 | await expect(page.getByTestId('grid-select')).toBeVisible(); 10 | await expect(page.getByTestId('table-select')).toBeVisible(); 11 | }); 12 | 13 | test('layout selector changes layout to grid and panels are visible', async ({ page }) => { 14 | await page.getByTestId('layout-selector').click(); 15 | await page.getByTestId('table-select').click(); 16 | await page.getByTestId('layout-selector').click(); 17 | await page.getByTestId('grid-select').click(); 18 | await expect(page.getByTestId('panel-content')).toBeVisible(); 19 | }); 20 | 21 | test('layout selector changes layout to table and rows are visible', async ({ page }) => { 22 | await page.getByTestId('layout-selector').click(); 23 | await page.getByTestId('table-select').click(); 24 | await expect(page.getByTestId('table-content')).toBeVisible(); 25 | }); 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "jsx": "react", 18 | "types": ["vite/client", "node"] 19 | }, 20 | "include": ["src"] 21 | } 22 | -------------------------------------------------------------------------------- /vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | declare const __VERSION__: string; 3 | -------------------------------------------------------------------------------- /vite.lib.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js' 4 | import viteTsconfigPaths from 'vite-tsconfig-paths' 5 | import * as pkg from './package.json'; 6 | 7 | export default defineConfig({ 8 | define: { 9 | __VERSION__: JSON.stringify(pkg.version), 10 | }, 11 | // depending on your application, base can also be "/" 12 | base: '', 13 | plugins: [react(), viteTsconfigPaths(), cssInjectedByJsPlugin()], 14 | build: { 15 | // sourcemap: 'hidden', 16 | chunkSizeWarningLimit: 3500, 17 | lib: { 18 | entry: 'src/index.tsx', 19 | name: 'trelliscope-viewer', 20 | fileName: 'trelliscope-viewer', 21 | }, 22 | rollupOptions: { 23 | external: ['react', 'react-dom'], 24 | // external: Object.keys(pkg.dependencies || {}), 25 | output: { 26 | globals: { 27 | react: "React", 28 | 'react-dom': "reactdom", 29 | }, 30 | }, 31 | }, 32 | }, 33 | server: { 34 | // this ensures that the browser opens upon server start 35 | open: true, 36 | // this sets a default port to 3000 37 | port: 3000, 38 | }, 39 | }) 40 | --------------------------------------------------------------------------------