diff --git a/changelog.d/1241-street-level-panel-cuts.md b/changelog.d/1241-street-level-panel-cuts.md index d10d0c613..1dd9dc5f5 100644 --- a/changelog.d/1241-street-level-panel-cuts.md +++ b/changelog.d/1241-street-level-panel-cuts.md @@ -1,5 +1,5 @@ - Street Level panel: SINCE is a dropdown of its nine windows (the tooltip names the cut-off date), IMAGERY and FIT / FILL are plain toggle buttons, - and the panel opens by itself only when a photo opens or fails to, no - longer when the layer is switched on. FIT / FILL starts at FIT each - session (gekh, #1241). + and the panel opens by itself only when a photo opens or a click fails to + load one, no longer when the layer is switched on. FIT / FILL starts at + FIT each session (gekh, #1241). diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 22df1c290..dc4b30d2c 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -3539,8 +3539,9 @@ image; FIT/FILL starts at FIT each session. IMAGERY and FIT/FILL are rows of toggle buttons (`aria-pressed`), each its own tab stop. SINCE is a select of the nine windows; a link's day count between them gets its own option, and the tooltip names the cut-off date. The panel opens by itself only when a -photo opens or fails to (a click on the map or a line would otherwise load -it, or its error, out of sight), never when the layer is switched on. +photo opens, or a click fails to open one or to load its sequence (the +result, or its error, would otherwise be out of sight), never when the layer +is switched on. MapillaryJS loads on the first photo. Opening one frames it with a single camera flight through the application's deferred navigation (`begin` / diff --git a/src/ui/streetLevelControls.js b/src/ui/streetLevelControls.js index 0b65b2583..e9bb3acfc 100644 --- a/src/ui/streetLevelControls.js +++ b/src/ui/streetLevelControls.js @@ -29,7 +29,7 @@ export class StreetLevelControls { this._state = null; this._view = null; this._wasOpen = false; - /** The open error last shown, so the same one never reopens the panel. */ + /** The error last shown: the same one never reopens the panel. */ this._streetError = null; /** The SINCE option for a day count between the stops, while one is set. */ this._customSince = null; @@ -432,17 +432,20 @@ export class StreetLevelControls { } /** - * Open the panel when an image opens, or fails to (a click on the map or a - * line would otherwise load it, or its error, out of sight). Never - * persisted: the stored collapse state stays the user's own choice. + * Open the panel when an image opens, or when a click fails to open one or + * to load its sequence (the result, or its error, would otherwise be out of + * sight). Never persisted: the stored collapse state stays the user's own + * choice. */ _reactToTransitions(view) { // Other size changes reach the viewer through the ResizeObserver; opening // asks for one resize because the element may not have a size yet. const open = view.viewer.open; - // A photo that failed to open (a line or ground click, OPEN) says why in - // the panel: it opens for that too, or the click would seem to do nothing. - const error = this._state?.street?.error || null; + // A photo that failed to open (a line or ground click, OPEN), or a + // Mapillary line whose sequence failed to load, says why in the panel: + // it opens for that too, or the click would seem to do nothing. + const error = + this._state?.street?.error || this._state?.sequence?.error || null; if ((open && !this._wasOpen) || (error && error !== this._streetError)) this.setCollapsed(false, { persist: false }); this._wasOpen = open; diff --git a/src/ui/streetLevelControls.test.mjs b/src/ui/streetLevelControls.test.mjs index f7d2f98cd..dd6b6c196 100644 --- a/src/ui/streetLevelControls.test.mjs +++ b/src/ui/streetLevelControls.test.mjs @@ -426,6 +426,7 @@ function uiState({ renderMode = 'letterbox', keyRequired = false, streetError = null, + sequenceError = null, } = {}) { return { enabled, @@ -434,7 +435,12 @@ function uiState({ keyRejected: false, filter: { pano, sinceDays }, coverage: { count: 3, loading: false, hint: '', error: null }, - sequence: { selectedId: null, images: 0, loading: false }, + sequence: { + selectedId: null, + images: 0, + loading: false, + error: sequenceError, + }, street: { open, loading: false, @@ -803,6 +809,18 @@ test('a photo that fails to open opens the panel too, so its error shows', () => controls.destroy(); })); +test('a sequence that fails to load opens the panel, so its error shows', () => + withDom(async (dom) => { + const { layer, calls, controls } = stubPanel(dom, uiState()); + // A Mapillary line click whose sequence lookup the Graph API refused. + layer.publish(uiState({ sequenceError: 'Mapillary did not answer' })); + assert.deepEqual(calls.collapsed, [[false, { persist: false }]]); + assert.equal(dom.root.querySelector('#sl-error').hidden, false); + layer.publish(uiState({ sequenceError: 'Mapillary did not answer' })); + assert.equal(calls.collapsed.length, 1, 'the same error opens it once'); + controls.destroy(); + })); + test('FIT / FILL show the selected render mode (P3)', () => withDom(async (dom) => { const { layer, controls } = stubPanel(dom, uiState({ open: true }));