Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log view: use initialOptions to save & restore view state on navigation #1688

Merged
merged 6 commits into from
Mar 11, 2024

Conversation

MetRonnie
Copy link
Member

@MetRonnie MetRonnie commented Feb 26, 2024

Follow-up to #1664
Moved from MetRonnie#7

The view state (filters, inputs, toggles etc.) can now be saved with the Lumino tab layout by using a prop called initialOptions.

This is only implemented for the log view for now.

Check List

  • I have read CONTRIBUTING.md and added my name as a Code Contributor.
  • Contains logically grouped changes (else tidy your branch by rebase).
  • Does not contain off-topic changes (use other PRs for other changes).
  • Tests are included
  • No changelog needed as entry for Save & restore workspace layout on navigation #1664 covers this
  • No docs PR needed

@MetRonnie MetRonnie added the UX/UI User experience and interface improvements label Feb 26, 2024
@MetRonnie MetRonnie added this to the 2.4.0 milestone Feb 26, 2024
@MetRonnie MetRonnie self-assigned this Feb 26, 2024
Comment on lines +375 to +390
// Watch id & file together:
this.$watch(
() => ({
id: this.id ?? undefined, // (treat null as undefined)
file: this.file ?? undefined
}),
async ({ id }, old) => {
// update the query when the id or file change
this.updateQuery()
// refresh the file list when the id changes
if (id !== old?.id) {
await this.updateLogFileList()
}
},
{ immediate: true }
)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rejigged the watchers for file and id to prevent race conditions. The usual order of events now is (for the example of opening the workflow log):

  1. id gets set for the first time to '~user/workflow'. file is nullish.

    • updateQuery() gets called because the id changed, but because file is nullish it doesn't really do anything

      cylc-ui/src/views/Log.vue

      Lines 429 to 431 in 58f5d96

      if (!this.file || !this.id) {
      this.query = null
      return
    • updateLogFileList() gets called because the id changed
  2. file has been set to the default scheduler log by updateLogFileList()

    • updateQuery() gets called again because file changed, and causes the log file to display
    • updateLogFileList() doesn't get called because id is unchanged

Comment on lines -416 to +473
if (this.file && !(this.file in logFiles)) {
if (this.file && !logFiles.includes(this.file)) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x in arr does not do what you think it does!

Comment on lines 51 to 62
export const useInitialOptions = (name, { props, emit }) => {
const _ref = ref(props.initialOptions[name])
watch(
_ref,
(val, old) => emit(
updateInitialOptionsEvent,
{ ...props.initialOptions, [name]: val }
),
{ immediate: true, deep: true }
)
return _ref
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is similar to using a writeable computed property e.g.

computed({
  get: () => props.initialOptions[name],
  set: (val) => emit(
    updateInitialOptionsEvent,
    { ...props.initialOptions, [name]: val }
  )
})

but that seemed to lead to the ref value not updating until the next tick? Maybe the watcher approach works because it is backed up by a concrete ref? Not sure

required: false,
default: () => {}
}
initialOptions,
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of the other views have an initialOptions prop - is this a solution that can be rolled out for table, tree and graph views as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the initialOptions prop can be added to them to allow storing their view states

emits: [
updateInitialOptionsEvent,
],

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the other views I think the initialOptions logic is inside src/components/cylc/view-name/view-name.vue
Should this be the case for log view as well for consistency?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is the views (in src/views/) that we mount in tabs in the workspace, so these are where we want the initialOptions prop to live.

You have correctly pointed out that in some cases, the component (in src/components) contains the state we want to save per view. We might need to refactor those to extract the state into the view, which would be passed into the component as a prop (probably using v-model).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the split between src/views and src/components. IMO:

Views are cylc-ui plugins, the view code should contain all the boiler plate for the plugin, e.g. the views name & icon, the logic for subscription handling, mappings onto the data store, user facing options and their defaults.

Components are generic reusable items that could, conceivably, find application in multiple views. They should contain the logic for their own rendering but be as ignorant as possible of the cylc-ui application itself (but will have to make assumptions about the format of the data they are provided).

Copy link
Member

@oliver-sanders oliver-sanders left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, tested the debounce and preservation of initial options.

src/views/initialOptions.js Outdated Show resolved Hide resolved
This is saved along with the Lumino layout for restoring after navigation.

Implemented for the log view only so far.
.invoke('val')
.should('eq', jobFile)
// Navigate away
cy.visit('/#/workspace/two')
Copy link
Contributor

@markgrahamdawson markgrahamdawson Mar 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we expect the state to persist when changing the route like this?
I would have thought this would cause all state to be lost unless stored in localstorage

Copy link
Member Author

@MetRonnie MetRonnie Mar 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The state is stored in the vuex store, which persists for the lifetime of the browser session until the user refreshes the page. This is because we handle navigation using vue-router so the app is continuously up while you navigate within it

Copy link
Contributor

@markgrahamdawson markgrahamdawson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I squash and merge this?

@MetRonnie MetRonnie merged commit b43c886 into cylc:master Mar 11, 2024
8 checks passed
@MetRonnie
Copy link
Member Author

MetRonnie commented Mar 11, 2024

If the commits are similar enough that it makes sense to squash, or there is only 1 commit, or the commits are not tidy. Otherwise in this case I think the granularity of the commits makes sense for a normal merge

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
UX/UI User experience and interface improvements
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants