From 77f8aeec402028aea2858cccc70f7a06a3fe7285 Mon Sep 17 00:00:00 2001
From: Pablo Mayrgundter
Date: Wed, 4 Dec 2024 23:25:16 +0100
Subject: [PATCH] UI Refresh: Apps, LeftDrawer, themed Typography, reorg;
Hot-reload esbuild and more (#1271)
* cypress: skip broken tests. tracked in issue #1269
* cypress: skip another test. tracked in issue #1269
* checkpoint
* apps: url state. sidebar: title padding cleanup. controlbutton: tooltips fixed. notes: avatar valign top, dateline linebreaks.
* tests passing.
* tests passing.
* layout: working in landscape; checkpoint
* layout: left side aligned
* unit tests pass
* lint passes
* versions: move fetch logic to hook, more testing
* SideDrawer: mobile bottom drawer mostly working
* cypress passing
* cypress passinger
* cypress passingerer
* Delete api.min.js
---------
Signed-off-by: Pablo Mayrgundter
---
.eslintrc.js | 2 +
cypress.config.js | 4 +-
cypress/e2e/appStore/appStore.cy.js | 17 -
cypress/e2e/apps/apps.cy.js | 17 +
cypress/e2e/notes-100/access-notes-list.cy.js | 3 +-
.../e2e/notes-100/access-shared-note.cy.js | 6 +-
cypress/e2e/notes-100/create-a-note.cy.js | 3 +-
cypress/e2e/notes-100/select-a-note.cy.js | 5 +-
cypress/e2e/open/100/open-model-dialog.cy.js | 9 +-
cypress/e2e/parallel.sh | 2 +-
.../e2e/placemarks-100/marker-selection.cy.js | 9 +-
.../placemarks-100/marker-visibility.cy.js | 20 +-
.../view-100/access-element-properties.cy.js | 3 +-
package.json | 13 +-
public/icons/mod_logo.jpeg | Bin 0 -> 4681 bytes
public/mod.html | 18 +
public/mod.js | 65 ++
public/widgets/mod.html | 112 ++++
src/BaseRoutes.jsx | 10 +-
src/Components/About/AboutControl.jsx | 5 +-
src/Components/About/AboutControl.test.jsx | 15 +-
src/Components/About/AboutDescription.jsx | 6 +-
src/Components/About/AboutDialog.jsx | 53 +-
src/Components/About/Discord.svg | 38 ++
src/Components/AppBar.jsx | 4 +-
src/Components/AppStore/AppStorePanel.jsx | 52 --
.../AppStore/AppStoreSideDrawerControl.jsx | 98 ---
src/Components/Apps/AppsControl.jsx | 29 +
.../AppsListing.jsx} | 56 +-
.../AppsMessagesHandler.js} | 0
src/Components/Apps/AppsPanel.jsx | 50 ++
.../AppsRegistry.json} | 9 +-
src/Components/Apps/hashState.js | 17 +
src/Components/Buttons.jsx | 54 +-
src/Components/Buttons.test.jsx | 56 +-
src/Components/Camera/hashState.js | 14 +
src/Components/ControlsGroup.jsx | 35 --
src/Components/CutPlane/CutPlaneMenu.jsx | 138 ++---
src/Components/CutPlane/CutPlaneMenu.test.jsx | 51 +-
src/Components/CutPlane/hashState.js | 103 ++++
src/Components/Dialog.jsx | 48 +-
.../{ElementGroup.jsx => ElementsControl.jsx} | 0
...roup.test.jsx => ElementsControl.test.jsx} | 18 +-
src/Components/Help/HelpControl.jsx | 12 +-
src/Components/Help/HelpControl.test.jsx | 8 +-
src/Components/LoadingBackdrop.jsx | 16 +-
src/Components/Logo/Logo.jsx | 35 +-
src/Components/Markers/MarkerControl.jsx | 137 +----
src/Components/Markers/hashState.js | 130 ++++
src/Components/NavTree/NavTree.jsx | 8 +-
src/Components/NavTree/NavTreeControl.jsx | 6 +-
src/Components/NavTree/NavTreeItem.jsx | 3 -
src/Components/NavTree/NavTreePanel.jsx | 61 +-
src/Components/NavTree/TypesNavTree.jsx | 5 +-
src/Components/NavTree/hashState.js | 8 +-
src/Components/Notes/NoteCard.jsx | 11 +-
src/Components/Notes/NoteCardCreate.jsx | 10 +-
src/Components/Notes/NoteContent.jsx | 3 +-
src/Components/Notes/NoteFooter.jsx | 25 +-
src/Components/Notes/Notes.jsx | 252 ++++----
src/Components/Notes/NotesControl.jsx | 2 +-
src/Components/Notes/NotesNavBar.jsx | 39 +-
src/Components/Notes/NotesPanel.jsx | 23 +-
src/Components/Notes/NotesPanel.test.jsx | 18 +
src/Components/Notes/component.js | 3 +
src/Components/Notes/hashState.js | 73 ++-
src/Components/Open/OpenModelControl.test.jsx | 5 +-
src/Components/Open/OpenModelDialog.jsx | 5 +-
src/Components/Open/PleaseLogin.jsx | 2 +-
src/Components/Open/component.js | 3 +
src/Components/OperationsGroup.jsx | 73 ---
src/Components/Profile/ProfileControl.jsx | 13 +-
src/Components/Properties/Properties.jsx | 63 +-
src/Components/Properties/PropertiesPanel.jsx | 39 +-
.../Properties/PropertiesPanel.test.jsx | 18 +
src/Components/Properties/component.js | 1 +
src/Components/Properties/hashState.js | 19 +-
src/Components/Search/SearchBar.jsx | 73 +--
src/Components/Share/ShareControl.jsx | 25 +-
src/Components/Share/hashState.js | 9 +
.../SideDrawer/HorizonResizerButton.jsx | 126 ++--
src/Components/SideDrawer/Panel.jsx | 114 ++--
src/Components/SideDrawer/PanelTitle.jsx | 46 --
src/Components/SideDrawer/PanelWithTitle.jsx | 39 --
src/Components/SideDrawer/SideDrawer.jsx | 107 ++--
src/Components/SideDrawer/SideDrawer.test.jsx | 62 +-
.../SideDrawer/SideDrawerPanels.test.jsx | 23 -
.../SideDrawer/VerticalResizerButton.jsx | 117 ++--
src/Components/TabbedDialog.fixture.jsx | 30 -
src/Components/TabbedDialog.jsx | 66 --
src/Components/TabbedDialog.test.jsx | 43 --
src/Components/Versions/VersionsControl.jsx | 5 +-
.../Versions/VersionsPanel.fixture.js | 9 +-
src/Components/Versions/VersionsPanel.jsx | 63 +-
.../Versions/VersionsPanel.test.jsx | 60 +-
...xture.jsx => VersionsTimeline.fixture.jsx} | 22 +-
src/Components/Versions/VersionsTimeline.jsx | 26 +-
.../Versions/VersionsTimeline.test.jsx | 37 +-
src/Components/Versions/useVersions.jsx | 50 ++
src/Components/Versions/useVersions.test.jsx | 21 +
src/Containers/AppsSideDrawer.jsx | 49 ++
src/Containers/BottomBar.jsx | 28 +
src/Containers/CadView.jsx | 58 +-
src/Containers/CadView.test.jsx | 11 +-
src/Containers/ControlsGroup.jsx | 49 ++
src/Containers/ControlsGroupAndDrawer.jsx | 112 ----
src/Containers/NavTreeAndVersionsDrawer.jsx | 85 +++
.../NavTreeAndVersionsDrawer.test.jsx | 85 +++
src/Containers/NotesAndPropertiesDrawer.jsx | 59 ++
.../NotesAndPropertiesDrawer.test.jsx | 45 ++
src/Containers/OperationsGroup.jsx | 66 ++
.../OperationsGroup.test.jsx | 0
src/Containers/OperationsGroupAndDrawer.jsx | 14 +-
src/Containers/RootLandscape.jsx | 87 +++
src/Containers/TabbedPanels.jsx | 246 ++++++++
src/Share.fixture.jsx | 14 +-
src/Share.jsx | 12 +-
src/Styles.jsx | 4 +-
src/WidgetApi/ApiConnection.js | 4 +-
src/WidgetApi/ApiConnectionIframe.js | 31 +-
src/WidgetApi/WidgetApi.js | 12 +-
src/__mocks__/api-handlers.js | 6 -
src/assets/LogoB.svg | 2 +-
src/assets/LogoBWithDomain.svg | 2 +-
src/assets/icons/AppStore.svg | 7 -
src/assets/icons/Copy.svg | 3 -
src/assets/icons/Discord.svg | 3 -
src/assets/icons/GitHub.svg | 3 -
src/assets/icons/Info.svg | 7 -
src/assets/icons/Information.svg | 3 -
src/assets/icons/Moon.svg | 3 -
src/assets/icons/Sun.svg | 3 -
src/assets/icons/Tree.svg | 16 +-
src/index.jsx | 54 +-
src/loader/Loader.js | 17 +-
src/loader/urls.js | 1 -
src/store/AppsSlice.js | 24 +
src/store/{AppSlice.js => BrowserSlice.js} | 17 +-
src/store/CutPlanesSlice.js | 25 +
src/store/NotesSlice.js | 114 ++--
src/store/OpenSlice.js | 7 +
src/store/ShareSlice.js | 13 +
src/store/SideDrawerSlice.jsx | 27 +-
src/store/UISlice.js | 26 -
src/store/useStore.js | 10 +-
src/store/useStore.test.js | 4 +-
src/theme/Components.js | 7 +-
src/theme/Palette.js | 4 +
src/theme/Theme.fixture.jsx | 6 +-
src/theme/Theme.jsx | 2 +
src/theme/Typography.js | 20 +-
tools/esbuild/common.js | 1 +
tools/esbuild/serve.js | 2 +
tools/esbuild/vars.cypress.js | 2 +
tools/esbuild/vars.prod.js | 7 +-
tools/jest/setupTests.js | 6 +-
tools/jest/vars.jest.js | 2 +
yarn.lock | 571 ++++++++++++++++--
158 files changed, 3511 insertions(+), 2156 deletions(-)
delete mode 100644 cypress/e2e/appStore/appStore.cy.js
create mode 100644 cypress/e2e/apps/apps.cy.js
create mode 100644 public/icons/mod_logo.jpeg
create mode 100644 public/mod.html
create mode 100644 public/mod.js
create mode 100644 public/widgets/mod.html
create mode 100755 src/Components/About/Discord.svg
delete mode 100644 src/Components/AppStore/AppStorePanel.jsx
delete mode 100644 src/Components/AppStore/AppStoreSideDrawerControl.jsx
create mode 100644 src/Components/Apps/AppsControl.jsx
rename src/Components/{AppStore/AppStoreListing.jsx => Apps/AppsListing.jsx} (66%)
rename src/Components/{AppStore/AppStoreMessagesHandler.js => Apps/AppsMessagesHandler.js} (100%)
create mode 100644 src/Components/Apps/AppsPanel.jsx
rename src/Components/{AppStore/AppStoreData.json => Apps/AppsRegistry.json} (50%)
create mode 100644 src/Components/Apps/hashState.js
delete mode 100644 src/Components/ControlsGroup.jsx
rename src/Components/{ElementGroup.jsx => ElementsControl.jsx} (100%)
rename src/Components/{ElementGroup.test.jsx => ElementsControl.test.jsx} (87%)
create mode 100644 src/Components/Notes/NotesPanel.test.jsx
create mode 100644 src/Components/Notes/component.js
create mode 100644 src/Components/Open/component.js
delete mode 100644 src/Components/OperationsGroup.jsx
create mode 100644 src/Components/Properties/PropertiesPanel.test.jsx
create mode 100644 src/Components/Properties/component.js
delete mode 100644 src/Components/SideDrawer/PanelTitle.jsx
delete mode 100644 src/Components/SideDrawer/PanelWithTitle.jsx
delete mode 100644 src/Components/SideDrawer/SideDrawerPanels.test.jsx
delete mode 100644 src/Components/TabbedDialog.fixture.jsx
delete mode 100644 src/Components/TabbedDialog.jsx
delete mode 100644 src/Components/TabbedDialog.test.jsx
rename src/Components/Versions/{Timeline.fixture.jsx => VersionsTimeline.fixture.jsx} (52%)
create mode 100644 src/Components/Versions/useVersions.jsx
create mode 100644 src/Components/Versions/useVersions.test.jsx
create mode 100644 src/Containers/AppsSideDrawer.jsx
create mode 100644 src/Containers/BottomBar.jsx
create mode 100644 src/Containers/ControlsGroup.jsx
delete mode 100644 src/Containers/ControlsGroupAndDrawer.jsx
create mode 100644 src/Containers/NavTreeAndVersionsDrawer.jsx
create mode 100644 src/Containers/NavTreeAndVersionsDrawer.test.jsx
create mode 100644 src/Containers/NotesAndPropertiesDrawer.jsx
create mode 100644 src/Containers/NotesAndPropertiesDrawer.test.jsx
create mode 100644 src/Containers/OperationsGroup.jsx
rename src/{Components => Containers}/OperationsGroup.test.jsx (100%)
create mode 100644 src/Containers/RootLandscape.jsx
create mode 100644 src/Containers/TabbedPanels.jsx
delete mode 100644 src/assets/icons/AppStore.svg
delete mode 100644 src/assets/icons/Copy.svg
delete mode 100755 src/assets/icons/Discord.svg
delete mode 100644 src/assets/icons/GitHub.svg
delete mode 100644 src/assets/icons/Info.svg
delete mode 100644 src/assets/icons/Information.svg
delete mode 100644 src/assets/icons/Moon.svg
delete mode 100644 src/assets/icons/Sun.svg
create mode 100644 src/store/AppsSlice.js
rename src/store/{AppSlice.js => BrowserSlice.js} (54%)
create mode 100644 src/store/CutPlanesSlice.js
create mode 100644 src/store/ShareSlice.js
diff --git a/.eslintrc.js b/.eslintrc.js
index 237cd3667..48c715834 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -37,6 +37,7 @@ module.exports = {
'react',
'jsx-a11y',
'jsdoc',
+ 'eslint-plugin-react-compiler',
],
rules: {
'arrow-parens': ['error', 'always'],
@@ -101,6 +102,7 @@ module.exports = {
'react/jsx-tag-spacing': ['error', {beforeSelfClosing: 'never'}],
'react/prop-types': 'off',
'react/self-closing-comp': 'error',
+ // TODO(pablo): re-enable.. got this down to 10. 'react-compiler/react-compiler': 'error',
'require-await': 'error',
'semi': ['error', 'never'],
'space-infix-ops': ['error'],
diff --git a/cypress.config.js b/cypress.config.js
index bbf0108d0..d524cc3f1 100644
--- a/cypress.config.js
+++ b/cypress.config.js
@@ -24,13 +24,13 @@ module.exports = import('./tools/esbuild/vars.cypress.js').then(({
env: {
// Used in support/models.js to setup intercepts, should match what code
// under tests will be using.
- // TODO(pablo): cypress chrome seems to not have OPFS, so using original
- // instead of RAW_GIT_PROXY_URL_NEW
AUTH0_DOMAIN: vars.AUTH0_DOMAIN,
GITHUB_BASE_URL: vars.GITHUB_BASE_URL,
GITHUB_BASE_URL_UNAUTHENTICATED: vars.GITHUB_BASE_URL_UNAUTHENTICATED,
MSW_IS_ENABLED: true,
OAUTH2_CLIENT_ID: vars.OAUTH2_CLIENT_ID,
+ // TODO(pablo): cypress chrome seems to not have OPFS, so using original
+ // instead of RAW_GIT_PROXY_URL_NEW
RAW_GIT_PROXY_URL: vars.RAW_GIT_PROXY_URL,
RAW_GIT_PROXY_URL_NEW: vars.RAW_GIT_PROXY_URL_NEW,
},
diff --git a/cypress/e2e/appStore/appStore.cy.js b/cypress/e2e/appStore/appStore.cy.js
deleted file mode 100644
index 68eb2127b..000000000
--- a/cypress/e2e/appStore/appStore.cy.js
+++ /dev/null
@@ -1,17 +0,0 @@
-describe('appStore side drawer', () => {
- context('enable/disable feature using url parameter', () => {
- beforeEach(() => {
- cy.setCookie('isFirstTime', '1')
- cy.visit('/')
- })
-
- it('should not show app-store icon when url parameter is not present', () => {
- cy.findByRole('button', {name: /Open App Store/}).should('not.exist')
- })
-
- it.skip('should show app-store icon when url parameter is present', () => {
- cy.routerNavigate('/share/v/p?feature=apps', {replace: true})
- cy.findByRole('button', {name: /Open App Store/}).should('exist')
- })
- })
-})
diff --git a/cypress/e2e/apps/apps.cy.js b/cypress/e2e/apps/apps.cy.js
new file mode 100644
index 000000000..5addcee62
--- /dev/null
+++ b/cypress/e2e/apps/apps.cy.js
@@ -0,0 +1,17 @@
+describe('apps side drawer', () => {
+ context('enable/disable feature using url parameter', () => {
+ beforeEach(() => {
+ cy.setCookie('isFirstTime', '1')
+ cy.visit('/')
+ })
+
+ it('should not show apps icon when url parameter is not present', () => {
+ cy.findByRole('button', {name: /Open Apps/}).should('not.exist')
+ })
+
+ it.skip('should show apps icon when url parameter is present', () => {
+ cy.routerNavigate('/share/v/p?feature=apps', {replace: true})
+ cy.findByRole('button', {name: /Open Apps/}).should('exist')
+ })
+ })
+})
diff --git a/cypress/e2e/notes-100/access-notes-list.cy.js b/cypress/e2e/notes-100/access-notes-list.cy.js
index 717721550..ca9d7d9f9 100644
--- a/cypress/e2e/notes-100/access-notes-list.cy.js
+++ b/cypress/e2e/notes-100/access-notes-list.cy.js
@@ -1,4 +1,5 @@
import '@percy/cypress'
+import {TITLE_NOTES} from '../../../src/Components/Notes/component'
import {homepageSetup, returningUserVisitsHomepageWaitForModel} from '../../support/utils'
@@ -11,7 +12,7 @@ describe('Notes 100: Access notes list', () => {
beforeEach(() => cy.get('[data-testid="control-button-notes"]').click())
it('Notes visible - Screen', () => {
cy.get('[data-testid="list-notes"]')
- cy.get('[data-testid="panelTitle"]').contains('NOTES')
+ cy.get(`[data-testid="PanelTitle-${TITLE_NOTES}"]`).contains(TITLE_NOTES)
})
})
})
diff --git a/cypress/e2e/notes-100/access-shared-note.cy.js b/cypress/e2e/notes-100/access-shared-note.cy.js
index cd9383449..21af7854f 100644
--- a/cypress/e2e/notes-100/access-shared-note.cy.js
+++ b/cypress/e2e/notes-100/access-shared-note.cy.js
@@ -1,4 +1,5 @@
import '@percy/cypress'
+import {TITLE_NOTE, TITLE_NOTES} from '../../../src/Components/Notes/component'
import {waitForModel, homepageSetup, setIsReturningUser} from '../../support/utils'
/** {@link https://github.com/bldrs-ai/Share/issues/1072} */
@@ -13,8 +14,7 @@ describe('Notes 100: Access shared note', () => {
waitForModel()
})
it('Notes open - Screen', () => {
- // Panel title to contain 'NOTES' string
- cy.get('[data-testid="panelTitle"]').contains('NOTES')
+ cy.get(`[data-testid="PanelTitle-${TITLE_NOTES}"]`).contains(TITLE_NOTES)
// List of notes to be visible
cy.get('.MuiList-root').should('exist')
cy.percySnapshot()
@@ -27,7 +27,7 @@ describe('Notes 100: Access shared note', () => {
})
it('Panel title to contain NOTE string and back button', () => {
- cy.get('[data-testid="panelTitle"]').contains('NOTE')
+ cy.get(`[data-testid="PanelTitle-${TITLE_NOTE}"]`).contains(TITLE_NOTE)
cy.get('[data-testid="Back to the list"]').should('exist')
})
diff --git a/cypress/e2e/notes-100/create-a-note.cy.js b/cypress/e2e/notes-100/create-a-note.cy.js
index 39b97a8fe..c267ee763 100644
--- a/cypress/e2e/notes-100/create-a-note.cy.js
+++ b/cypress/e2e/notes-100/create-a-note.cy.js
@@ -1,4 +1,5 @@
import '@percy/cypress'
+import {TITLE_NOTE_ADD} from '../../../src/Components/Notes/component'
import {
auth0Login,
homepageSetup,
@@ -19,7 +20,7 @@ describe('Notes 100: Create a note', () => {
it('Notes list switches to display only create note card and back to the list when nav backbutton is pressed', () => {
cy.get('[data-testid="Back to the list"]').should('exist')
cy.get('[placeholder="Note Title"]').should('exist')
- cy.get('[data-testid="panelTitle"]').contains('ADD A NOTE')
+ cy.get(`[data-testid="PanelTitle-${TITLE_NOTE_ADD}"]`).contains(TITLE_NOTE_ADD)
cy.percySnapshot()
cy.get('[data-testid="Back to the list"]').click()
cy.get('[data-testid="list-notes"]').should('exist')
diff --git a/cypress/e2e/notes-100/select-a-note.cy.js b/cypress/e2e/notes-100/select-a-note.cy.js
index 247d8889c..c4d928101 100644
--- a/cypress/e2e/notes-100/select-a-note.cy.js
+++ b/cypress/e2e/notes-100/select-a-note.cy.js
@@ -1,4 +1,5 @@
import '@percy/cypress'
+import {TITLE_NOTE} from '../../../src/Components/Notes/component'
import {
homepageSetup,
returningUserVisitsHomepageWaitForModel,
@@ -22,7 +23,9 @@ describe('Notes 100: Select a note', () => {
cy.get('[data-testid="list-notes"] > :nth-child(4) > [data-testid="note-card"] p').contains('testComment_1')
cy.get('[data-testid="list-notes"] > :nth-child(5) > [data-testid="note-card"] p').contains('testComment_2')
- cy.get('[data-testid="panelTitle"]').should('have.text', 'NOTE')
+ cy.get(`[data-testid="PanelTitle-${TITLE_NOTE}"]`).debug()
+
+ cy.get(`[data-testid="PanelTitle-${TITLE_NOTE}"]`).should('have.text', TITLE_NOTE)
cy.get('[data-testid="Back to the list"]').click()
diff --git a/cypress/e2e/open/100/open-model-dialog.cy.js b/cypress/e2e/open/100/open-model-dialog.cy.js
index b8e4bd734..f670e2cd6 100644
--- a/cypress/e2e/open/100/open-model-dialog.cy.js
+++ b/cypress/e2e/open/100/open-model-dialog.cy.js
@@ -1,13 +1,14 @@
import '@percy/cypress'
+import {LABEL_GITHUB} from '../../../../src/Components/Open/component'
import {
auth0Login,
homepageSetup,
returningUserVisitsHomepageWaitForModel,
} from '../../../support/utils'
import {
- setupVirtualPathIntercept,
- waitForModelReady,
- } from '../../../support/models'
+ setupVirtualPathIntercept,
+ waitForModelReady,
+} from '../../../support/models'
/** {@link https://github.com/bldrs-ai/Share/issues/1159}*/
@@ -47,7 +48,7 @@ describe('Open 100: Open model dialog', () => {
const interceptTag = 'ghOpenModelLoad'
it('Choose the path to the model on GitHub -> model is loaded into the scene', () => {
cy.get('[data-testid="tab-github"]').click()
- cy.findByText('Github').click()
+ cy.findByText(LABEL_GITHUB).click()
cy.findByLabelText('Organization', {timeout: 5000}).click()
cy.contains('@cypresstester').click()
cy.findByLabelText('Repository', {timeout: 5000}).eq(0).click()
diff --git a/cypress/e2e/parallel.sh b/cypress/e2e/parallel.sh
index 4fc941a00..91a5d67bb 100755
--- a/cypress/e2e/parallel.sh
+++ b/cypress/e2e/parallel.sh
@@ -18,7 +18,7 @@ run_cy_spec() {
echo "Running cypress specs in parallel..."
# Misc
-run_cy_spec misc cypress/e2e/appStore,cypress/e2e/hide-feat,cypress/e2e/home,cypress/e2e/ifc-model,cypress/e2e/integration
+run_cy_spec misc cypress/e2e/apps,cypress/e2e/hide-feat,cypress/e2e/home,cypress/e2e/ifc-model,cypress/e2e/integration
# Then conventional
for EPIC in create-100 open notes-100 profile-100 versions-100 view-100 ; do
diff --git a/cypress/e2e/placemarks-100/marker-selection.cy.js b/cypress/e2e/placemarks-100/marker-selection.cy.js
index b3201e4b7..c016eb82a 100644
--- a/cypress/e2e/placemarks-100/marker-selection.cy.js
+++ b/cypress/e2e/placemarks-100/marker-selection.cy.js
@@ -1,5 +1,6 @@
import '@percy/cypress'
import {Raycaster, Vector2, Vector3} from 'three'
+import {TITLE_NOTES} from '../../../src/Components/Notes/component'
import {homepageSetup,
returningUserVisitsHomepageWaitForModel,
auth0Login,
@@ -18,13 +19,13 @@ describe('Placemarks 100: Not visible when notes is not open', () => {
beforeEach(() => {
cy.get('[data-testid="control-button-notes"]').click()
cy.get('[data-testid="list-notes"]')
- cy.get('[data-testid="panelTitle"]').contains('NOTES')
-
+ cy.get(`[data-testid="PanelTitle-${TITLE_NOTES}"]`).contains(TITLE_NOTES)
cy.window().then((window) => {
win = window
})
- // eslint-disable-next-line cypress/no-unnecessary-waiting, no-magic-numbers
- cy.wait(1000)
+ const waitTimeMs = 1000
+ // eslint-disable-next-line cypress/no-unnecessary-waiting
+ cy.wait(waitTimeMs)
})
it('should select a marker and url hash should change', () => {
const {markerObjects, camera, domElement} = win.markerScene
diff --git a/cypress/e2e/placemarks-100/marker-visibility.cy.js b/cypress/e2e/placemarks-100/marker-visibility.cy.js
index 2f4ea8328..651483560 100644
--- a/cypress/e2e/placemarks-100/marker-visibility.cy.js
+++ b/cypress/e2e/placemarks-100/marker-visibility.cy.js
@@ -1,4 +1,5 @@
import '@percy/cypress'
+import {TITLE_NOTES} from '../../../src/Components/Notes/component'
import {homepageSetup, returningUserVisitsHomepageWaitForModel} from '../../support/utils'
@@ -8,23 +9,22 @@ describe('Placemarks 100: Not visible when notes is not open', () => {
context('Returning user visits homepage', () => {
beforeEach(returningUserVisitsHomepageWaitForModel)
it('MarkerControl should not exist', () => {
- cy.get('[data-testid="markerControl"]').should('not.exist')
+ cy.get('[data-testid="markerControl"]').should('not.exist')
})
context('Open Notes and MarkerControl should exist', () => {
- let win
+ let win
beforeEach(() => {
cy.get('[data-testid="control-button-notes"]').click()
-
cy.get('[data-testid="list-notes"]')
- cy.get('[data-testid="panelTitle"]').contains('NOTES')
-
+ cy.get(`[data-testid="PanelTitle-${TITLE_NOTES}"]`).contains(TITLE_NOTES)
cy.window().then((window) => {
- win = window
+ win = window
})
- // eslint-disable-next-line cypress/no-unnecessary-waiting, no-magic-numbers
- cy.wait(1500)
- })
+ const waitTimeMs = 1500
+ // eslint-disable-next-line cypress/no-unnecessary-waiting
+ cy.wait(waitTimeMs)
+ })
it('MarkerControl should exist', () => {
// Access the scene objects
const markers = win.markerScene.markerObjects
@@ -34,7 +34,7 @@ describe('Placemarks 100: Not visible when notes is not open', () => {
// Check visibility of markers
markers.forEach((marker) => {
- // eslint-disable-next-line no-unused-expressions
+ // eslint-disable-next-line no-unused-expressions
expect(marker.userData.id).to.exist
})
diff --git a/cypress/e2e/view-100/access-element-properties.cy.js b/cypress/e2e/view-100/access-element-properties.cy.js
index a5f9ee347..96ce1db03 100644
--- a/cypress/e2e/view-100/access-element-properties.cy.js
+++ b/cypress/e2e/view-100/access-element-properties.cy.js
@@ -1,4 +1,5 @@
import '@percy/cypress'
+import {TITLE} from '../../../src/Components/Properties/component'
import {
homepageSetup,
setIsReturningUser,
@@ -26,7 +27,7 @@ describe('View 100: Access elements property', () => {
it('Side drawer containing properties shall be visible', () => {
cy.get('[data-testid="control-button-properties"]').should('be.visible')
- cy.get('[data-testid="panelTitle"]').contains('PROPERTIES')
+ cy.get(`[data-testid="PanelTitle-${TITLE}"]`).contains(TITLE)
cy.percySnapshot()
})
})
diff --git a/package.json b/package.json
index 679479219..e91323159 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "bldrs",
- "version": "1.0.1165",
+ "version": "1.0.1181",
"main": "src/index.jsx",
"license": "AGPL-3.0",
"homepage": "https://github.com/bldrs-ai/Share",
@@ -67,12 +67,13 @@
"@bldrs-ai/ifclib": "5.3.3",
"@emotion/react": "11.10.0",
"@emotion/styled": "11.10.0",
+ "@fontsource/roboto": "^5.1.0",
"@iconscout/react-unicons": "2.0.0",
- "@mui/icons-material": "5.11.9",
+ "@mui/icons-material": "5.16.8",
"@mui/lab": "5.0.0-alpha.95",
- "@mui/material": "5.10.1",
- "@mui/styled-engine": "5.10.1",
- "@mui/styles": "5.11.13",
+ "@mui/material": "5.16.8",
+ "@mui/styled-engine": "5.16.8",
+ "@mui/styles": "5.16.8",
"@octokit/rest": "20.1.1",
"@sentry/react": "7.61.0",
"@sentry/tracing": "7.61.0",
@@ -88,6 +89,7 @@
"esbuild-copy-static-files": "0.1.0",
"esbuild-plugin-progress": "1.0.1",
"esbuild-plugin-svgr": "2.0.0",
+ "eslint-plugin-react-compiler": "^19.0.0-beta-df7b47d-20241124",
"html-webpack-plugin": "5.5.3",
"js-cookie": "3.0.5",
"material-ui-popup-state": "5.0.4",
@@ -127,7 +129,6 @@
"@testing-library/dom": "8.19.1",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "13.4.0",
- "@testing-library/react-hooks": "8.0.1",
"@tsconfig/recommended": "1.0.1",
"@types/react": "18.0.26",
"@types/react-dom": "18.0.9",
diff --git a/public/icons/mod_logo.jpeg b/public/icons/mod_logo.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..dc11f35c1bbd275607b46788c6a399b610df1634
GIT binary patch
literal 4681
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
literal 0
HcmV?d00001
diff --git a/public/mod.html b/public/mod.html
new file mode 100644
index 000000000..7cf2cb6b5
--- /dev/null
+++ b/public/mod.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
diff --git a/public/mod.js b/public/mod.js
new file mode 100644
index 000000000..8313e0c31
--- /dev/null
+++ b/public/mod.js
@@ -0,0 +1,65 @@
+const {ClientWidgetApi, PostmessageTransport} = mxwidgets()
+
+
+/** @param {string} msg */
+function log(msg) {
+ const logElt = document.getElementById('logItems')
+ logElt.innerHTML += `${msg}`
+}
+
+// Define the widget configuration
+const widget = {
+ id: 'bldrs-share',
+ name: 'Embedded Share App',
+ type: 'm.custom',
+ url: 'https://example.com/widget',
+ creatorUserId: '@user:matrix.org',
+ origin: '*',
+}
+
+// Reference the iframe element
+const iframe = document.getElementById('share-app')
+
+// Create a PostmessageTransport driver
+const driver = new PostmessageTransport(window, iframe.contentWindow)
+
+// Instantiate the ClientWidgetApi
+const clientWidgetApi = new ClientWidgetApi(widget, iframe, driver)
+
+console.log('clientWidgetApi', clientWidgetApi)
+
+clientWidgetApi.on('ready', () => {
+ console.log('ON READY')
+ clientWidgetApi.updateVisibility(true).then(() => console.log('Widget knows it is visible now'))
+ // clientWidgetApi.transport.send('com.example.my_action', {isExample: true})
+})
+
+/** @param {string} eventName */
+function register(eventName, cb) {
+ console.log('registering:', eventName)
+ clientWidgetApi.on(`action:ai.bldrs-share.${eventName}`, (event) => {
+ cb(eventName, event)
+ // clientWidgetApi.transport.reply(event, {success: true, response: 'Acknowledged!'})
+ })
+}
+
+const events = [
+ 'ChangeViewSettings',
+ 'HiddenElements',
+ 'ModelLoaded',
+ 'ChangeViewSettings',
+ 'LoadModel',
+ 'HighlightElements',
+ 'SelectElements',
+ 'UnhideElements',
+ 'HideElements',
+]
+events.forEach((name) => {
+ register(name, (eventName, e) => console.log(`HANDLER ON ${eventName}:`, e.detail))
+})
+
+register('SelectionChanged', (eventName, e) => {
+ const data = e.detail.data
+ const currentSelection = data.current[0]
+ log(`GUID: ${currentSelection}`)
+})
diff --git a/public/widgets/mod.html b/public/widgets/mod.html
new file mode 100644
index 000000000..e1a2b4286
--- /dev/null
+++ b/public/widgets/mod.html
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+ MOD Parts App
+
+
+
+
+
+ Message received is shown here:
+
+
+
+
+
+
+
diff --git a/src/BaseRoutes.jsx b/src/BaseRoutes.jsx
index 30cb60aaa..d41227989 100644
--- a/src/BaseRoutes.jsx
+++ b/src/BaseRoutes.jsx
@@ -1,13 +1,13 @@
import React, {useEffect} from 'react'
import {Outlet, Route, Routes, useLocation, useNavigate} from 'react-router-dom'
-import ShareRoutes from './ShareRoutes'
-import {checkOPFSAvailability, setUpGlobalDebugFunctions} from './OPFS/utils'
-import debug from './utils/debug'
-import {navWith} from './utils/navigate'
-import useStore from './store/useStore'
import * as Sentry from '@sentry/react'
import {useAuth0} from './Auth0/Auth0Proxy'
+import {checkOPFSAvailability, setUpGlobalDebugFunctions} from './OPFS/utils'
+import ShareRoutes from './ShareRoutes'
import {initializeOctoKitAuthenticated, initializeOctoKitUnauthenticated} from './net/github/OctokitExport'
+import useStore from './store/useStore'
+import debug from './utils/debug'
+import {navWith} from './utils/navigate'
const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes)
diff --git a/src/Components/About/AboutControl.jsx b/src/Components/About/AboutControl.jsx
index 348a37b55..a2cc9ad8c 100644
--- a/src/Components/About/AboutControl.jsx
+++ b/src/Components/About/AboutControl.jsx
@@ -34,7 +34,7 @@ export default function AboutControl() {
setIsDialogDisplayed={setIsAboutVisible}
hashPrefix={HASH_PREFIX_ABOUT}
placement='right'
- buttonTestId='control-button-about'
+ dataTestId={testId}
>
)
}
+
+
+export const testId = 'control-button-about'
diff --git a/src/Components/About/AboutControl.test.jsx b/src/Components/About/AboutControl.test.jsx
index a42c6c184..4bbcdb222 100644
--- a/src/Components/About/AboutControl.test.jsx
+++ b/src/Components/About/AboutControl.test.jsx
@@ -3,11 +3,10 @@ import React from 'react'
import {fireEvent, render, waitFor} from '@testing-library/react'
import {HelmetStoreRouteThemeCtx} from '../../Share.fixture'
import * as FirstTime from '../../privacy/firstTime'
-import AboutControl from './AboutControl'
-import PkgJson from '../../../package.json'
+import AboutControl, {testId} from './AboutControl'
+import {BLDRS_MISSION} from './AboutDialog'
-const bldrsVersionString = `Bldrs: ${PkgJson.version}`
describe('AboutControl', () => {
beforeEach(() => {
Cookies.remove(FirstTime.COOKIE_NAME)
@@ -15,21 +14,21 @@ describe('AboutControl', () => {
it('renders the AboutControl button', () => {
const {getByTestId} = render(, {wrapper: HelmetStoreRouteThemeCtx})
- const aboutControl = getByTestId('control-button-about')
+ const aboutControl = getByTestId(testId)
expect(aboutControl).toBeInTheDocument()
})
it('renders AboutDialog when control is pressed', () => {
- const {getByTitle, getByText} = render(, {wrapper: HelmetStoreRouteThemeCtx})
- const aboutControl = getByTitle(bldrsVersionString)
+ const {getByTestId, getByText} = render(, {wrapper: HelmetStoreRouteThemeCtx})
+ const aboutControl = getByTestId(testId)
fireEvent.click(aboutControl)
- const dialogTitle = getByText('Build every thing together')
+ const dialogTitle = getByText(BLDRS_MISSION)
expect(dialogTitle).toBeInTheDocument()
})
it('updates the document title when the dialog is open', async () => {
const {getByTestId} = render(, {wrapper: HelmetStoreRouteThemeCtx})
- const aboutControl = getByTestId('control-button-about')
+ const aboutControl = getByTestId(testId)
fireEvent.click(aboutControl)
await(waitFor(() => expect(document.title).toBe('About — bldrs.ai')))
})
diff --git a/src/Components/About/AboutDescription.jsx b/src/Components/About/AboutDescription.jsx
index cb025ca5c..71e72b5cd 100644
--- a/src/Components/About/AboutDescription.jsx
+++ b/src/Components/About/AboutDescription.jsx
@@ -1,13 +1,13 @@
-import React from 'react'
+import React, {ReactElement} from 'react'
import Box from '@mui/material/Box'
import Typography from '@mui/material/Typography'
-import useTheme from '@mui/styles/useTheme'
+import {useTheme} from '@mui/material/styles'
/**
* A miniature view of the App to show as a guide in the About dialog.
*
- * @return {React.ReactComponent}
+ * @return {ReactElement}
*/
export default function AboutDescription({setIsDialogDisplayed}) {
const theme = useTheme()
diff --git a/src/Components/About/AboutDialog.jsx b/src/Components/About/AboutDialog.jsx
index c8a7b6f9f..1c9b3e115 100644
--- a/src/Components/About/AboutDialog.jsx
+++ b/src/Components/About/AboutDialog.jsx
@@ -2,6 +2,7 @@ import React, {ReactElement} from 'react'
import {Helmet} from 'react-helmet-async'
import Link from '@mui/material/Link'
import Stack from '@mui/material/Stack'
+import SvgIcon from '@mui/material/SvgIcon'
import Typography from '@mui/material/Typography'
import Dialog from '../Dialog'
import {LogoBWithDomain} from '../Logo/Logo'
@@ -9,12 +10,10 @@ import {LogoBWithDomain} from '../Logo/Logo'
// import PrivacyControl from './PrivacyControl'
import GitHubIcon from '@mui/icons-material/GitHub'
import EmailIcon from '@mui/icons-material/Email'
-import DiscordIcon from '../../assets/icons/Discord.svg'
+import DiscordIcon from './Discord.svg'
/**
- * The AboutDialog component
- *
* @property {boolean} isDialogDisplayed Passed to Dialog to be controlled
* @property {Function} setIsDialogDisplayed Passed to Dialog to be controlled
* @property {Function} onClose Callback when closed
@@ -24,16 +23,19 @@ export default function AboutDialog({isDialogDisplayed, setIsDialogDisplayed, on
return (
),
- ({loremIpsum(2)}
),
- ({loremIpsum(4)}
),
- ]}
- actionCbs={[
- () => debug().log('clicked 1'),
- () => debug().log('clicked 2'),
- () => debug().log('clicked 3'),
- ]}
- isDialogDisplayed={true}
- setIsDialogDisplayed={() => debug().log('setIsDialogDisplayed')}
- />
-
-)
diff --git a/src/Components/TabbedDialog.jsx b/src/Components/TabbedDialog.jsx
deleted file mode 100644
index 730e22827..000000000
--- a/src/Components/TabbedDialog.jsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import React, {ReactElement, useState} from 'react'
-import MuiDialog from '@mui/material/Dialog'
-import DialogActions from '@mui/material/DialogActions'
-import DialogContent from '@mui/material/DialogContent'
-import DialogTitle from '@mui/material/DialogTitle'
-import {assertDefined, assertArraysEqualLength} from '../utils/assert'
-import {CloseButton, RectangularButton} from './Buttons'
-import Tabs from './Tabs'
-
-
-/**
- * A Dialog with tabs to page between associated contents.
- *
- * @property {Array} tabLabels Tab names
- * @property {Array} headerLabels Short messages describing the current operation
- * @property {Array} contentComponents Components coresponding to the tabs
- * @property {Array} actionCbs Callbacks for each component's ok button
- * @property {boolean} isDialogDisplayed React var
- * @property {Function} setIsDialogDisplayed React setter
- * @property {boolean} [isTabsScrollable] Activate if the number of tabs is larger than 5
- * @property {ReactElement} [icon] Leading icon above header description
- * @property {string} [actionButtonLabels] Labels for action ok buttons
- * @return {ReactElement}
- */
-export default function TabbedDialog({
- tabLabels,
- headerLabels,
- contentComponents,
- actionCbs,
- isDialogDisplayed,
- setIsDialogDisplayed,
- isTabsScrollable = false,
- icon,
- actionButtonLabels,
-}) {
- assertDefined(tabLabels, headerLabels, contentComponents, actionCbs, isDialogDisplayed, setIsDialogDisplayed)
- assertArraysEqualLength(tabLabels, headerLabels, contentComponents, actionCbs)
- const onClose = () => setIsDialogDisplayed(false)
- const [currentTab, setCurrentTab] = useState(0)
- return (
-
-
-
- {icon && <>{icon}
>}
- {headerLabels[currentTab]}
-
-
-
-
-
- {contentComponents[currentTab]}
-
-
-
-
-
- )
-}
diff --git a/src/Components/TabbedDialog.test.jsx b/src/Components/TabbedDialog.test.jsx
deleted file mode 100644
index ad5968395..000000000
--- a/src/Components/TabbedDialog.test.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import React from 'react'
-import {render, screen, fireEvent} from '@testing-library/react'
-import TabbedDialog from './TabbedDialog'
-
-
-describe('TabbedDialog', () => {
- it('', () => {
- const cb1 = jest.fn()
- const cb2 = jest.fn()
- const cb3 = jest.fn()
- render(
- {'A content'}),
- ({'B content'}
),
- ({'C content'}
),
- ]}
- actionCbs={[cb1, cb2, cb3]}
- actionButtonLabels={['A OK', 'B OK', 'C OK']}
- isDialogDisplayed={true}
- setIsDialogDisplayed={jest.fn()}
- />)
-
- fireEvent.click(screen.getByText('A OK'))
- expect(cb1.mock.calls.length).toBe(1)
- expect(cb2.mock.calls.length).toBe(0)
- expect(cb3.mock.calls.length).toBe(0)
-
- fireEvent.click(screen.getByText('Open'))
- fireEvent.click(screen.getByText('B OK'))
- expect(cb1.mock.calls.length).toBe(1)
- expect(cb2.mock.calls.length).toBe(1)
- expect(cb3.mock.calls.length).toBe(0)
-
- fireEvent.click(screen.getByText('Save'))
- fireEvent.click(screen.getByText('C OK'))
- expect(cb1.mock.calls.length).toBe(1)
- expect(cb2.mock.calls.length).toBe(1)
- expect(cb3.mock.calls.length).toBe(1)
- })
-})
diff --git a/src/Components/Versions/VersionsControl.jsx b/src/Components/Versions/VersionsControl.jsx
index f62d57f06..725b87ac4 100644
--- a/src/Components/Versions/VersionsControl.jsx
+++ b/src/Components/Versions/VersionsControl.jsx
@@ -2,6 +2,7 @@ import React, {ReactElement} from 'react'
import useStore from '../../store/useStore'
import {ControlButtonWithHashState} from '../Buttons'
import {HASH_PREFIX_VERSIONS} from './hashState'
+import {TITLE} from './VersionsPanel'
import HistoryIcon from '@mui/icons-material/History'
@@ -15,12 +16,12 @@ export default function VersionsControl() {
const setIsVersionsVisible = useStore((state) => state.setIsVersionsVisible)
return (
}
isDialogDisplayed={isVersionsVisible}
setIsDialogDisplayed={setIsVersionsVisible}
hashPrefix={HASH_PREFIX_VERSIONS}
- placement='bottom'
+ placement='right'
/>
)
}
diff --git a/src/Components/Versions/VersionsPanel.fixture.js b/src/Components/Versions/VersionsPanel.fixture.js
index d69666ac7..0de5e2899 100644
--- a/src/Components/Versions/VersionsPanel.fixture.js
+++ b/src/Components/Versions/VersionsPanel.fixture.js
@@ -2,10 +2,17 @@ const RAW_GIT_PROXY_URL_NEW = process.env.RAW_GIT_PROXY_URL_NEW
export const MOCK_MODEL_PATH_GIT = {
- org: 'user2',
+ orgName: 'user2',
repo: 'Schneestock-Public',
branch: 'main',
filepath: '/ZGRAGGEN.ifc',
eltPath: '',
gitpath: `${RAW_GIT_PROXY_URL_NEW}/user2/Schneestock-Public/main/ZGRAGGEN.ifc`,
+ getRepoPath: () => '/main/blob/ZGRAGGEN.ifc',
+}
+
+
+export const MOCK_REPOSITORY = {
+ orgName: 'testOrg',
+ name: 'testRepo',
}
diff --git a/src/Components/Versions/VersionsPanel.jsx b/src/Components/Versions/VersionsPanel.jsx
index 8ce2a2067..049bd87dc 100644
--- a/src/Components/Versions/VersionsPanel.jsx
+++ b/src/Components/Versions/VersionsPanel.jsx
@@ -1,16 +1,18 @@
-import React, {ReactElement, useState, useEffect} from 'react'
+import React, {ReactElement} from 'react'
import {useNavigate} from 'react-router-dom'
import useStore from '../../store/useStore'
-import {getCommitsForFile} from '../../net/github/Commits'
import {assertDefined} from '../../utils/assert'
-import debug from '../../utils/debug'
import {navigateBaseOnModelPath} from '../../utils/location'
import {TooltipIconButton} from '../Buttons'
import Panel from '../SideDrawer/Panel'
import VersionsTimeline from './VersionsTimeline'
+import useVersions from './useVersions'
import RestartAltIcon from '@mui/icons-material/RestartAlt'
+export const TITLE = 'Versions'
+
+
/**
* VersionsPanel displays a series of versions in a timeline format.
* Each version corresponds to a commit, and this component fetches
@@ -22,37 +24,12 @@ import RestartAltIcon from '@mui/icons-material/RestartAlt'
*/
export default function VersionsPanel({filePath, currentRef}) {
assertDefined(filePath, currentRef)
+ const navigate = useNavigate()
const accessToken = useStore((state) => state.accessToken)
const repository = useStore((state) => state.repository)
const modelPath = useStore((state) => state.modelPath)
const setIsVersionsVisible = useStore((state) => state.setIsVersionsVisible)
-
- const [commitData, setCommitData] = useState([])
-
- const navigate = useNavigate()
-
- useEffect(() => {
- const fetchCommits = async () => {
- try {
- const commits = await getCommitsForFile(repository, filePath, accessToken)
- if (commits) {
- const versionsInfo = commits.map((entry) => {
- const extractedData = {
- authorName: entry.commit.author.name,
- commitMessage: entry.commit.message,
- commitDate: entry.commit.author.date,
- sha: entry.sha,
- }
- return extractedData
- })
- setCommitData(versionsInfo)
- }
- } catch (error) {
- debug().log(error)
- }
- }
- fetchCommits()
- }, [repository, filePath, accessToken])
+ const {commits, loading, error} = useVersions({accessToken, repository, filePath})
/**
@@ -61,7 +38,7 @@ export default function VersionsPanel({filePath, currentRef}) {
* @param {number} index active commit index
*/
function navigateToCommit(index) {
- const sha = commitData[index].sha
+ const sha = commits[index].sha
if (modelPath) {
const commitPath =
navigateBaseOnModelPath(modelPath.org, modelPath.repo, sha, modelPath.filepath)
@@ -80,11 +57,10 @@ export default function VersionsPanel({filePath, currentRef}) {
}
}
-
return (
}
@@ -93,15 +69,18 @@ export default function VersionsPanel({filePath, currentRef}) {
size='small'
/>
}
- sx={{m: '0 0 0 10px'}} // equal to SearchBar m:5 + p:5
- onCloseClick={() => setIsVersionsVisible(false)}
- data-testid='Version Panel'
+ onClose={() => setIsVersionsVisible(false)}
+ data-testid='VersionsPanel'
>
-
+ <>
+ {loading && <>Loading...>}
+ {error && <>Error: error>}
+
+ >
)
}
diff --git a/src/Components/Versions/VersionsPanel.test.jsx b/src/Components/Versions/VersionsPanel.test.jsx
index 5d0c94389..0b665ca33 100644
--- a/src/Components/Versions/VersionsPanel.test.jsx
+++ b/src/Components/Versions/VersionsPanel.test.jsx
@@ -1,23 +1,69 @@
import React from 'react'
-import {render, renderHook, act} from '@testing-library/react'
-import ShareMock from '../../ShareMock'
+import {act, render, renderHook, waitFor} from '@testing-library/react'
+import {StoreRouteThemeCtx} from '../../Share.fixture'
import useStore from '../../store/useStore'
+import VersionsPanel, {TITLE} from './VersionsPanel'
import {
MOCK_MODEL_PATH_GIT,
MOCK_REPOSITORY,
} from './VersionsPanel.fixture'
-import VersionsPanel from './VersionsPanel'
+import useVersions from './useVersions'
+import {MOCK_COMMITS} from './VersionsTimeline.fixture'
+
+
+jest.mock('./useVersions')
describe('VersionsPanel', () => {
it('renders the panel', async () => {
+ // Simulated hook state
+ let mockCommitsState = {
+ commits: [],
+ loading: true,
+ }
+
+ // Mock useCommits to return the current state
+ useVersions.mockImplementation(() => mockCommitsState)
+
+ // Also setup store state
const {result} = renderHook(() => useStore((state) => state))
await act(() => {
+ result.current.setAccessToken('')
+ result.current.setIsVersionsVisible(true)
+ result.current.setRepository(MOCK_REPOSITORY.orgName, MOCK_REPOSITORY.name)
result.current.setModelPath(MOCK_MODEL_PATH_GIT)
- result.current.setRepository(MOCK_REPOSITORY)
})
- const {getByText} = render(, {wrapper: ShareMock})
- const dialogTitle = getByText('Versions')
- expect(dialogTitle).toBeInTheDocument()
+
+ // Render the component
+ const {getByText, rerender} = render(
+ ,
+ {wrapper: StoreRouteThemeCtx},
+ )
+
+ // Ensure loading state is rendered initially
+ await waitFor(() => {
+ expect(getByText('Versions')).toBeInTheDocument()
+ })
+
+ // Transition the state
+ mockCommitsState = {
+ commits: MOCK_COMMITS,
+ loading: false,
+ }
+
+ // Re-render to simulate the state update
+ rerender(
+ ,
+ {wrapper: StoreRouteThemeCtx})
+
+ // Wait for the updated state to be rendered
+ await waitFor(() => {
+ expect(getByText(TITLE)).toBeInTheDocument()
+ })
+ MOCK_COMMITS.forEach((commit) => {
+ expect(getByText(commit.authorName)).toBeInTheDocument()
+ expect(getByText(commit.commitDate)).toBeInTheDocument()
+ expect(getByText(commit.commitMessage)).toBeInTheDocument()
+ })
})
})
diff --git a/src/Components/Versions/Timeline.fixture.jsx b/src/Components/Versions/VersionsTimeline.fixture.jsx
similarity index 52%
rename from src/Components/Versions/Timeline.fixture.jsx
rename to src/Components/Versions/VersionsTimeline.fixture.jsx
index d0b1dcdf0..734022df7 100644
--- a/src/Components/Versions/Timeline.fixture.jsx
+++ b/src/Components/Versions/VersionsTimeline.fixture.jsx
@@ -3,21 +3,25 @@ import {ThemeCtx} from '../../theme/Theme.fixture'
import VersionsTimeline from './VersionsTimeline'
-const commitData = [
- {authorName: 'Version1',
+export const MOCK_COMMITS = [
+ {
+ authorName: 'testAuthor1',
commitDate: '09.17.2023',
commitMessage: 'commit 1',
},
- {authorName: 'Version1',
- commitDate: '09.17.2023',
+ {
+ authorName: 'testAuthor2',
+ commitDate: '09.18.2023',
commitMessage: 'commit 2',
},
- {authorName: 'Version1',
- commitDate: '09.17.2023',
+ {
+ authorName: 'testAuthor3',
+ commitDate: '09.19.2023',
commitMessage: 'commit 3',
},
- {authorName: 'Version1',
- commitDate: '09.17.2023',
+ {
+ authorName: 'testAuthor4',
+ commitDate: '09.20.2023',
commitMessage: 'commit 4',
},
]
@@ -25,6 +29,6 @@ const commitData = [
export default (
-
+
)
diff --git a/src/Components/Versions/VersionsTimeline.jsx b/src/Components/Versions/VersionsTimeline.jsx
index 97dffd099..06e955aa1 100644
--- a/src/Components/Versions/VersionsTimeline.jsx
+++ b/src/Components/Versions/VersionsTimeline.jsx
@@ -1,15 +1,15 @@
import React, {ReactElement, useState, useEffect} from 'react'
import Timeline from '@mui/lab/Timeline'
-import TimelineDot from '@mui/lab/TimelineDot'
-import TimelineItem from '@mui/lab/TimelineItem'
import TimelineConnector from '@mui/lab/TimelineConnector'
import TimelineContent from '@mui/lab/TimelineContent'
-import TimelineSeparator from '@mui/lab/TimelineSeparator'
+import TimelineDot from '@mui/lab/TimelineDot'
+import TimelineItem from '@mui/lab/TimelineItem'
import TimelineOppositeContent from '@mui/lab/TimelineOppositeContent'
+import TimelineSeparator from '@mui/lab/TimelineSeparator'
import Paper from '@mui/material/Paper'
import Stack from '@mui/material/Stack'
import Typography from '@mui/material/Typography'
-import useTheme from '@mui/styles/useTheme'
+import {useTheme} from '@mui/material/styles'
import {styled} from '@mui/system'
import Loader from '../Loader'
import NoContent from '../NoContent'
@@ -21,34 +21,34 @@ import CommitIcon from '@mui/icons-material/Commit'
* Each version corresponds to a commit, and this component fetches
* commit data for the provided branch and displays it.
*
- * @property {Array